Filters done, working on tests.

This commit is contained in:
Dessalines 2024-12-18 17:47:11 -05:00
parent 4586947910
commit 389083418c

View file

@ -9,11 +9,13 @@ use crate::structs::{
SearchCombinedViewInternal, SearchCombinedViewInternal,
}; };
use diesel::{ use diesel::{
dsl::not,
result::Error, result::Error,
BoolExpressionMethods, BoolExpressionMethods,
ExpressionMethods, ExpressionMethods,
JoinOnDsl, JoinOnDsl,
NullableExpressionMethods, NullableExpressionMethods,
PgTextExpressionMethods,
QueryDsl, QueryDsl,
SelectableHelper, SelectableHelper,
}; };
@ -21,7 +23,7 @@ use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder; use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{ use lemmy_db_schema::{
aliases::creator_community_actions, aliases::creator_community_actions,
newtypes::PersonId, newtypes::{CommunityId, PersonId},
schema::{ schema::{
comment, comment,
comment_actions, comment_actions,
@ -43,8 +45,10 @@ use lemmy_db_schema::{
combined::search::{search_combined_keys as key, SearchCombined}, combined::search::{search_combined_keys as key, SearchCombined},
community::CommunityFollower, community::CommunityFollower,
}, },
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool}, utils::{actions, actions_alias, functions::coalesce, fuzzy_search, get_conn, DbPool},
InternalToCombinedView, InternalToCombinedView,
ListingType,
SearchType,
}; };
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
@ -84,12 +88,20 @@ impl SearchCombinedPaginationCursor {
#[derive(Clone)] #[derive(Clone)]
pub struct PaginationCursorData(SearchCombined); pub struct PaginationCursorData(SearchCombined);
#[derive(derive_new::new)] #[derive(Default)]
pub struct SearchCombinedQuery { pub struct SearchCombinedQuery {
pub creator_id: PersonId, pub search_term: Option<String>,
#[new(default)] pub community_id: Option<CommunityId>,
pub creator_id: Option<PersonId>,
pub type_: Option<SearchType>,
// TODO sorts need to be added
// pub sort: Option<PostSortType>,
pub listing_type: Option<ListingType>,
pub title_only: Option<bool>,
pub post_url_only: Option<bool>,
pub liked_only: Option<bool>,
pub disliked_only: Option<bool>,
pub page_after: Option<PaginationCursorData>, pub page_after: Option<PaginationCursorData>,
#[new(default)]
pub page_back: Option<bool>, pub page_back: Option<bool>,
} }
@ -132,7 +144,7 @@ impl SearchCombinedQuery {
// For example, the creator must be the person table joined to either: // For example, the creator must be the person table joined to either:
// - post.creator_id // - post.creator_id
// - comment.creator_id // - comment.creator_id
let query = search_combined::table let mut query = search_combined::table
// The comment // The comment
.left_join(comment::table.on(comment_join)) .left_join(comment::table.on(comment_join))
// The post // The post
@ -217,6 +229,112 @@ impl SearchCombinedQuery {
)) ))
.into_boxed(); .into_boxed();
// The filters
// The search term
if let Some(search_term) = &self.search_term {
if self.post_url_only.unwrap_or_default() {
query = query.filter(post::url.eq(search_term));
} else {
let searcher = fuzzy_search(search_term);
let name_or_title_filter = post::name
.ilike(searcher.clone())
.or(comment::content.ilike(searcher.clone()))
.or(community::name.ilike(searcher.clone()))
.or(community::title.ilike(searcher.clone()))
.or(person::name.ilike(searcher.clone()))
.or(person::display_name.ilike(searcher.clone()));
let body_or_description_filter = post::body
.ilike(searcher.clone())
.or(community::description.ilike(searcher.clone()));
query = if self.title_only.unwrap_or_default() {
query.filter(name_or_title_filter)
} else {
query.filter(name_or_title_filter.or(body_or_description_filter))
}
}
}
// Community id
if let Some(community_id) = self.community_id {
query = query.filter(community::id.eq(community_id));
}
// Creator id
if let Some(creator_id) = self.creator_id {
query = query.filter(item_creator.eq(creator_id));
}
// Liked / disliked filter
if let Some(my_id) = my_person_id {
let not_creator_filter = item_creator.ne(my_id);
// TODO do I need not null checks for these?
let liked_disliked_filter = |score: i16| {
search_combined::post_id
.is_not_null()
.and(post_actions::like_score.eq(score))
.or(
search_combined::comment_id
.is_not_null()
.and(comment_actions::like_score.eq(score)),
)
};
if self.liked_only.unwrap_or_default() {
query = query
.filter(not_creator_filter)
.filter(liked_disliked_filter(1));
} else if self.disliked_only.unwrap_or_default() {
query = query
.filter(not_creator_filter)
.filter(liked_disliked_filter(-1));
}
};
// Type
if let Some(type_) = self.type_ {
query = match type_ {
SearchType::All => query,
SearchType::Posts => query.filter(search_combined::post_id.is_not_null()),
SearchType::Comments => query.filter(search_combined::comment_id.is_not_null()),
SearchType::Communities => query.filter(search_combined::community_id.is_not_null()),
SearchType::Users => query.filter(search_combined::person_id.is_not_null()),
}
}
// Listing type
// TODO need null checks for this one?
let is_subscribed = community_actions::followed.is_not_null();
match self.listing_type.unwrap_or_default() {
ListingType::Subscribed => query = query.filter(is_subscribed),
ListingType::Local => {
query = query
.filter(community::local.eq(true))
.filter(community::hidden.eq(false).or(is_subscribed));
}
ListingType::All => query = query.filter(community::hidden.eq(false).or(is_subscribed)),
ListingType::ModeratorView => {
query = query.filter(community_actions::became_moderator.is_not_null());
}
}
// Deleted / removed filters
// TODO need null checks for this?
query = query.filter(not(
comment::removed
.or(comment::deleted)
.or(post::removed)
.or(post::deleted)
.or(community::removed)
.or(community::deleted)
.or(community::removed)
.or(community::deleted)
.or(person::deleted),
));
let mut query = PaginatedQueryBuilder::new(query); let mut query = PaginatedQueryBuilder::new(query);
let page_after = self.page_after.map(|c| c.0); let page_after = self.page_after.map(|c| c.0);
@ -311,164 +429,159 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
} }
} }
// #[cfg(test)] #[cfg(test)]
// #[expect(clippy::indexing_slicing)] #[expect(clippy::indexing_slicing)]
// mod tests { mod tests {
// use crate::{ use crate::{combined::search_combined_view::SearchCombinedQuery, structs::SearchCombinedView};
// combined::search_combined_view::SearchCombinedQuery, use lemmy_db_schema::{
// structs::SearchCombinedView, assert_length,
// }; source::{
// use lemmy_db_schema::{ comment::{Comment, CommentInsertForm},
// source::{ community::{Community, CommunityInsertForm},
// comment::{Comment, CommentInsertForm}, instance::Instance,
// community::{Community, CommunityInsertForm}, person::{Person, PersonInsertForm},
// instance::Instance, post::{Post, PostInsertForm},
// person::{Person, PersonInsertForm}, },
// post::{Post, PostInsertForm}, traits::Crud,
// }, utils::{build_db_pool_for_tests, DbPool},
// traits::Crud, };
// utils::{build_db_pool_for_tests, DbPool}, use lemmy_utils::error::LemmyResult;
// }; use pretty_assertions::assert_eq;
// use lemmy_utils::error::LemmyResult; use serial_test::serial;
// use pretty_assertions::assert_eq;
// use serial_test::serial;
// struct Data { struct Data {
// instance: Instance, instance: Instance,
// timmy: Person, timmy: Person,
// sara: Person, sara: Person,
// timmy_post: Post, community: Community,
// timmy_post_2: Post, community_2: Community,
// sara_post: Post, timmy_post: Post,
// timmy_comment: Comment, timmy_post_2: Post,
// sara_comment: Comment, sara_post: Post,
// sara_comment_2: Comment, timmy_comment: Comment,
// } sara_comment: Comment,
sara_comment_2: Comment,
}
// async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> { async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
// let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
// let timmy_form = PersonInsertForm::test_form(instance.id, "timmy_pcv"); let timmy_form = PersonInsertForm::test_form(instance.id, "timmy_pcv");
// let timmy = Person::create(pool, &timmy_form).await?; let timmy = Person::create(pool, &timmy_form).await?;
// let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv"); let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv");
// let sara = Person::create(pool, &sara_form).await?; let sara = Person::create(pool, &sara_form).await?;
// let community_form = CommunityInsertForm::new( let community_form = CommunityInsertForm::new(
// instance.id, instance.id,
// "test community pcv".to_string(), "asklemmy".to_string(),
// "nada".to_owned(), "Ask Lemmy".to_owned(),
// "pubkey".to_string(), "pubkey".to_string(),
// ); );
// let community = Community::create(pool, &community_form).await?; let community = Community::create(pool, &community_form).await?;
// let timmy_post_form = PostInsertForm::new("timmy post prv".into(), timmy.id, community.id); let community_form_2 = CommunityInsertForm::new(
// let timmy_post = Post::create(pool, &timmy_post_form).await?; instance.id,
"startrek_ds9".to_string(),
"Star Trek - Deep Space Nine".to_owned(),
"pubkey".to_string(),
);
let community_2 = Community::create(pool, &community_form_2).await?;
// let timmy_post_form_2 = PostInsertForm::new("timmy post prv 2".into(), timmy.id, let timmy_post_form = PostInsertForm::new("timmy post prv".into(), timmy.id, community.id);
// community.id); let timmy_post_2 = Post::create(pool, &timmy_post_form_2).await?; let timmy_post = Post::create(pool, &timmy_post_form).await?;
// let sara_post_form = PostInsertForm::new("sara post prv".into(), sara.id, community.id); let timmy_post_form_2 = PostInsertForm::new("timmy post prv 2".into(), timmy.id, community.id);
// let sara_post = Post::create(pool, &sara_post_form).await?; let timmy_post_2 = Post::create(pool, &timmy_post_form_2).await?;
// let timmy_comment_form = let sara_post_form = PostInsertForm::new("sara post prv".into(), sara.id, community.id);
// CommentInsertForm::new(timmy.id, timmy_post.id, "timmy comment prv".into()); let sara_post = Post::create(pool, &sara_post_form).await?;
// let timmy_comment = Comment::create(pool, &timmy_comment_form, None).await?;
// let sara_comment_form = let timmy_comment_form =
// CommentInsertForm::new(sara.id, timmy_post.id, "sara comment prv".into()); CommentInsertForm::new(timmy.id, timmy_post.id, "timmy comment prv".into());
// let sara_comment = Comment::create(pool, &sara_comment_form, None).await?; let timmy_comment = Comment::create(pool, &timmy_comment_form, None).await?;
// let sara_comment_form_2 = let sara_comment_form =
// CommentInsertForm::new(sara.id, timmy_post_2.id, "sara comment prv 2".into()); CommentInsertForm::new(sara.id, timmy_post.id, "sara comment prv".into());
// let sara_comment_2 = Comment::create(pool, &sara_comment_form_2, None).await?; let sara_comment = Comment::create(pool, &sara_comment_form, None).await?;
// Ok(Data { let sara_comment_form_2 =
// instance, CommentInsertForm::new(sara.id, timmy_post_2.id, "sara comment prv 2".into());
// timmy, let sara_comment_2 = Comment::create(pool, &sara_comment_form_2, None).await?;
// sara,
// timmy_post,
// timmy_post_2,
// sara_post,
// timmy_comment,
// sara_comment,
// sara_comment_2,
// })
// }
// async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> { Ok(Data {
// Instance::delete(pool, data.instance.id).await?; instance,
timmy,
sara,
community,
community_2,
timmy_post,
timmy_post_2,
sara_post,
timmy_comment,
sara_comment,
sara_comment_2,
})
}
// Ok(()) async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> {
// } Instance::delete(pool, data.instance.id).await?;
// #[tokio::test] Ok(())
// #[serial] }
// async fn test_combined() -> LemmyResult<()> {
// let pool = &build_db_pool_for_tests();
// let pool = &mut pool.into();
// let data = init_data(pool).await?;
// // Do a batch read of timmy #[tokio::test]
// let timmy_content = SearchCombinedQuery::new(data.timmy.id) #[serial]
// .list(pool, &None) async fn community_search() -> LemmyResult<()> {
// .await?; let pool = &build_db_pool_for_tests();
// assert_eq!(3, timmy_content.len()); let pool = &mut pool.into();
let data = init_data(pool).await?;
// // Make sure the types are correct // Community search
// if let SearchCombinedView::Comment(v) = &timmy_content[0] { let community_search = SearchCombinedQuery::default().list(pool, &None).await?;
// assert_eq!(data.timmy_comment.id, v.comment.id); assert_length!(2, community_search);
// assert_eq!(data.timmy.id, v.creator.id);
// } else {
// panic!("wrong type");
// }
// if let SearchCombinedView::Post(v) = &timmy_content[1] {
// assert_eq!(data.timmy_post_2.id, v.post.id);
// assert_eq!(data.timmy.id, v.post.creator_id);
// } else {
// panic!("wrong type");
// }
// if let SearchCombinedView::Post(v) = &timmy_content[2] {
// assert_eq!(data.timmy_post.id, v.post.id);
// assert_eq!(data.timmy.id, v.post.creator_id);
// } else {
// panic!("wrong type");
// }
// // Do a batch read of sara // Make sure the types are correct
// let sara_content = SearchCombinedQuery::new(data.sara.id) if let SearchCombinedView::Community(v) = &community_search[0] {
// .list(pool, &None) assert_eq!(data.community_2.id, v.community.id);
// .await?; } else {
// assert_eq!(3, sara_content.len()); panic!("wrong type");
}
// // Make sure the report types are correct if let SearchCombinedView::Community(v) = &community_search[1] {
// if let SearchCombinedView::Comment(v) = &sara_content[0] { assert_eq!(data.community.id, v.community.id);
// assert_eq!(data.sara_comment_2.id, v.comment.id); } else {
// assert_eq!(data.sara.id, v.creator.id); panic!("wrong type");
// // This one was to timmy_post_2 }
// assert_eq!(data.timmy_post_2.id, v.post.id);
// assert_eq!(data.timmy.id, v.post.creator_id);
// } else {
// panic!("wrong type");
// }
// if let SearchCombinedView::Comment(v) = &sara_content[1] {
// assert_eq!(data.sara_comment.id, v.comment.id);
// assert_eq!(data.sara.id, v.creator.id);
// assert_eq!(data.timmy_post.id, v.post.id);
// assert_eq!(data.timmy.id, v.post.creator_id);
// } else {
// panic!("wrong type");
// }
// if let SearchCombinedView::Post(v) = &sara_content[2] {
// assert_eq!(data.sara_post.id, v.post.id);
// assert_eq!(data.sara.id, v.post.creator_id);
// } else {
// panic!("wrong type");
// }
// cleanup(data, pool).await?; // Filtered by id
let community_search_by_id = SearchCombinedQuery {
community_id: Some(data.community.id),
..Default::default()
}
.list(pool, &None)
.await?;
assert_length!(1, community_search_by_id);
// Ok(()) // Using a term
// } let community_search_by_name = SearchCombinedQuery {
// } search_term: Some("Ask".into()),
..Default::default()
}
.list(pool, &None)
.await?;
assert_length!(1, community_search_by_name);
if let SearchCombinedView::Community(v) = &community_search_by_name[0] {
// The asklemmy community
assert_eq!(data.community.id, v.community.id);
} else {
panic!("wrong type");
}
cleanup(data, pool).await?;
Ok(())
}
}