use super::UserDetails;
use crate::db::RedisOps;
use crate::{models::traits::Collection, types::DynError};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
pub const USER_NAME_KEY_PARTS: [&str; 2] = ["Users", "Name"];
#[derive(Serialize, Deserialize, ToSchema, Default)]
pub struct UserSearch(pub Vec<String>);
impl RedisOps for UserSearch {}
impl UserSearch {
pub async fn get_by_name(
name: &str,
skip: Option<usize>,
limit: Option<usize>,
) -> Result<Option<Self>, DynError> {
let elements = Self::get_from_index(name, skip, limit).await?;
if let Some(elements) = elements {
let user_ids: Vec<String> = elements
.into_iter()
.filter_map(|element| {
element.split(':').next_back().map(|p| p.to_string())
})
.collect();
return Ok(Some(UserSearch(user_ids)));
}
Ok(None)
}
pub async fn get_from_index(
name: &str,
skip: Option<usize>,
limit: Option<usize>,
) -> Result<Option<Vec<String>>, DynError> {
let name = name.to_lowercase();
let min = format!("[{name}"); let max = format!("({name}~");
Self::try_from_index_sorted_set_lex(&USER_NAME_KEY_PARTS, &min, &max, skip, limit).await
}
pub async fn put_to_index(details_list: &[&UserDetails]) -> Result<(), DynError> {
Self::delete_existing_records(
details_list
.iter()
.map(|details| details.id.as_str())
.collect::<Vec<&str>>()
.as_slice(),
)
.await?;
let mut items: Vec<(f64, String)> = Vec::with_capacity(details_list.len());
for details in details_list {
if details.name == "[DELETED]" {
break;
}
let username = details.name.to_lowercase();
let user_id = &details.id;
let score = 0.0;
let member = format!("{username}:{user_id}");
items.push((score, member));
}
Self::put_index_sorted_set(
&USER_NAME_KEY_PARTS,
&items
.iter()
.map(|(score, member)| (*score, member.as_str()))
.collect::<Vec<_>>(),
None,
None,
)
.await
}
async fn delete_existing_records(user_ids: &[&str]) -> Result<(), DynError> {
if user_ids.is_empty() {
return Ok(());
}
let mut records_to_delete: Vec<String> = Vec::with_capacity(user_ids.len());
let keys = user_ids
.iter()
.map(|&id| vec![id])
.collect::<Vec<Vec<&str>>>();
let users = UserDetails::get_from_index(keys.iter().map(|item| item.as_slice()).collect())
.await?
.into_iter()
.flatten()
.collect::<Vec<UserDetails>>();
for user_id in user_ids {
let existing_username = users
.iter()
.find(|user| user.id.to_string() == *user_id)
.map(|user| user.name.to_lowercase());
if let Some(existing_record) = existing_username {
let search_key = format!("{existing_record}:{user_id}");
records_to_delete.push(search_key);
}
}
Self::remove_from_index_sorted_set(
None,
&USER_NAME_KEY_PARTS,
records_to_delete
.iter()
.map(|item| item.as_str())
.collect::<Vec<&str>>()
.as_slice(),
)
.await?;
Ok(())
}
}