feat(search): implement search filtering in repository SQL queries
Wire SearchFilter into each repository's list method with entity-specific searchable columns: roasters (name, country, city), roasts (name, roaster, origin, producer), bags (roaster, roast), brews (roast, roaster), gear (make, model). Timeline passes None.
This commit is contained in:
parent
f616958e3c
commit
1e151123a0
6 changed files with 47 additions and 3 deletions
|
|
@ -160,7 +160,10 @@ impl BagRepository for SqlBagRepository {
|
|||
&self,
|
||||
filter: BagFilter,
|
||||
request: &ListRequest<BagSortKey>,
|
||||
search: Option<&str>,
|
||||
) -> Result<Page<BagWithRoast>, RepositoryError> {
|
||||
use crate::infrastructure::repositories::pagination::SearchFilter;
|
||||
|
||||
let order_clause = Self::order_clause(request);
|
||||
|
||||
// Build WHERE clause from filter
|
||||
|
|
@ -172,16 +175,21 @@ impl BagRepository for SqlBagRepository {
|
|||
};
|
||||
|
||||
let count_query = match &where_clause {
|
||||
Some(w) => format!("SELECT COUNT(*) FROM bags b WHERE {w}"),
|
||||
None => "SELECT COUNT(*) FROM bags".to_string(),
|
||||
Some(w) => format!(
|
||||
"SELECT COUNT(*) FROM bags b JOIN roasts r ON b.roast_id = r.id JOIN roasters rr ON r.roaster_id = rr.id WHERE {w}"
|
||||
),
|
||||
None => "SELECT COUNT(*) FROM bags b JOIN roasts r ON b.roast_id = r.id JOIN roasters rr ON r.roaster_id = rr.id".to_string(),
|
||||
};
|
||||
|
||||
let sf = search.and_then(|t| SearchFilter::new(t, vec!["rr.name", "r.name"]));
|
||||
|
||||
crate::infrastructure::repositories::pagination::paginate(
|
||||
&self.pool,
|
||||
request,
|
||||
&base_query,
|
||||
&count_query,
|
||||
&order_clause,
|
||||
sf.as_ref(),
|
||||
|record| Ok(Self::to_domain_with_roast(record)),
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -197,7 +197,10 @@ impl BrewRepository for SqlBrewRepository {
|
|||
&self,
|
||||
filter: BrewFilter,
|
||||
request: &ListRequest<BrewSortKey>,
|
||||
search: Option<&str>,
|
||||
) -> Result<Page<BrewWithDetails>, RepositoryError> {
|
||||
use crate::infrastructure::repositories::pagination::SearchFilter;
|
||||
|
||||
let order_clause = Self::order_clause(request);
|
||||
let where_clause = Self::build_where_clause(&filter);
|
||||
|
||||
|
|
@ -209,6 +212,8 @@ impl BrewRepository for SqlBrewRepository {
|
|||
let count_base = r"
|
||||
SELECT COUNT(*) FROM brews br
|
||||
JOIN bags b ON br.bag_id = b.id
|
||||
JOIN roasts r ON b.roast_id = r.id
|
||||
JOIN roasters rr ON r.roaster_id = rr.id
|
||||
";
|
||||
|
||||
let count_query = match &where_clause {
|
||||
|
|
@ -216,12 +221,15 @@ impl BrewRepository for SqlBrewRepository {
|
|||
None => count_base.to_string(),
|
||||
};
|
||||
|
||||
let sf = search.and_then(|t| SearchFilter::new(t, vec!["r.name", "rr.name"]));
|
||||
|
||||
crate::infrastructure::repositories::pagination::paginate(
|
||||
&self.pool,
|
||||
request,
|
||||
&base_query,
|
||||
&count_query,
|
||||
&order_clause,
|
||||
sf.as_ref(),
|
||||
|record| Ok(Self::to_domain_with_details(record)),
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -100,7 +100,10 @@ impl GearRepository for SqlGearRepository {
|
|||
&self,
|
||||
filter: GearFilter,
|
||||
request: &ListRequest<GearSortKey>,
|
||||
search: Option<&str>,
|
||||
) -> Result<Page<Gear>, RepositoryError> {
|
||||
use crate::infrastructure::repositories::pagination::SearchFilter;
|
||||
|
||||
let order_clause = Self::order_clause(request);
|
||||
let where_clause = Self::build_where_clause(&filter);
|
||||
|
||||
|
|
@ -118,12 +121,15 @@ impl GearRepository for SqlGearRepository {
|
|||
None => "SELECT COUNT(*) FROM gear".to_string(),
|
||||
};
|
||||
|
||||
let sf = search.and_then(|t| SearchFilter::new(t, vec!["make", "model"]));
|
||||
|
||||
crate::infrastructure::repositories::pagination::paginate(
|
||||
&self.pool,
|
||||
request,
|
||||
&base_query,
|
||||
&count_query,
|
||||
&order_clause,
|
||||
sf.as_ref(),
|
||||
Self::to_domain,
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -190,11 +190,16 @@ impl RoasterRepository for SqlRoasterRepository {
|
|||
async fn list(
|
||||
&self,
|
||||
request: &ListRequest<RoasterSortKey>,
|
||||
search: Option<&str>,
|
||||
) -> Result<Page<Roaster>, RepositoryError> {
|
||||
use crate::infrastructure::repositories::pagination::SearchFilter;
|
||||
|
||||
let order_clause = Self::order_clause(request);
|
||||
let base_query =
|
||||
"SELECT id, name, slug, country, city, homepage, notes, created_at FROM roasters";
|
||||
let count_query = "SELECT COUNT(*) FROM roasters";
|
||||
let sf =
|
||||
search.and_then(|t| SearchFilter::new(t, vec!["name", "country", "COALESCE(city,'')"]));
|
||||
|
||||
crate::infrastructure::repositories::pagination::paginate(
|
||||
&self.pool,
|
||||
|
|
@ -202,6 +207,7 @@ impl RoasterRepository for SqlRoasterRepository {
|
|||
base_query,
|
||||
count_query,
|
||||
&order_clause,
|
||||
sf.as_ref(),
|
||||
|record| Ok(Self::into_domain(record)),
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -249,10 +249,24 @@ impl RoastRepository for SqlRoastRepository {
|
|||
async fn list(
|
||||
&self,
|
||||
request: &ListRequest<RoastSortKey>,
|
||||
search: Option<&str>,
|
||||
) -> Result<Page<RoastWithRoaster>, RepositoryError> {
|
||||
use crate::infrastructure::repositories::pagination::SearchFilter;
|
||||
|
||||
let order_clause = Self::order_clause(request);
|
||||
let base_query = "SELECT r.id, r.roaster_id, r.name, r.slug, r.origin, r.region, r.producer, r.process, r.tasting_notes, r.created_at, ro.name AS roaster_name, ro.slug AS roaster_slug \n FROM roasts r \n JOIN roasters ro ON ro.id = r.roaster_id";
|
||||
let count_query = "SELECT COUNT(*) FROM roasts";
|
||||
let count_query = "SELECT COUNT(*) FROM roasts r JOIN roasters ro ON ro.id = r.roaster_id";
|
||||
let sf = search.and_then(|t| {
|
||||
SearchFilter::new(
|
||||
t,
|
||||
vec![
|
||||
"r.name",
|
||||
"ro.name",
|
||||
"COALESCE(r.origin,'')",
|
||||
"COALESCE(r.producer,'')",
|
||||
],
|
||||
)
|
||||
});
|
||||
|
||||
crate::infrastructure::repositories::pagination::paginate(
|
||||
&self.pool,
|
||||
|
|
@ -260,6 +274,7 @@ impl RoastRepository for SqlRoastRepository {
|
|||
base_query,
|
||||
count_query,
|
||||
&order_clause,
|
||||
sf.as_ref(),
|
||||
|record: RoastWithRoasterRecord| record.into_with_roaster(),
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ impl TimelineEventRepository for SqlTimelineEventRepository {
|
|||
base_query,
|
||||
count_query,
|
||||
&order_clause,
|
||||
None,
|
||||
|record: TimelineEventRecord| record.into_domain(),
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
Loading…
Reference in a new issue