search in admin also

This commit is contained in:
Priec
2026-06-22 21:38:03 +02:00
parent 1e66bfd657
commit 5a474f3474
7 changed files with 80 additions and 28 deletions

View File

@@ -138,10 +138,17 @@ async fn show(
.all(&ctx.db)
.await?;
let list = products::Entity::find()
.order_by_asc(products::Column::Name)
.all(&ctx.db)
.await?;
// Optional text search (drafts included), otherwise the whole catalog by
// name. Reuses the storefront's hybrid full-text + fuzzy product search.
let query = params.get("q").map(String::as_str).unwrap_or("").trim().to_string();
let list = if query.is_empty() {
products::Entity::find()
.order_by_asc(products::Column::Name)
.all(&ctx.db)
.await?
} else {
products::Entity::search(&ctx.db, &query, 1000, false).await?
};
// Category sidebar tree (counts over the full, unfiltered product list) plus
// the active `?category=` filter applied to the rows.
@@ -212,6 +219,7 @@ async fn show(
"products": rows,
"category_groups": category_groups,
"selected_category": selected_category,
"query": query,
"total_count": list.len(),
"uncategorized_count": category_ids.iter().filter(|c| c.is_none()).count(),
"error": params.get("error"),

View File

@@ -281,10 +281,17 @@ async fn index(
.map(|c| (c.id, c.name.clone()))
.collect();
let list = products::Entity::find()
.order_by_desc(products::Column::CreatedAt)
.all(&ctx.db)
.await?;
// Optional text search (drafts included), otherwise the full catalog newest
// first. Reuses the storefront's hybrid full-text + fuzzy product search.
let query = params.get("q").map(String::as_str).unwrap_or("").trim().to_string();
let list = if query.is_empty() {
products::Entity::find()
.order_by_desc(products::Column::CreatedAt)
.all(&ctx.db)
.await?
} else {
products::Entity::search(&ctx.db, &query, 1000, false).await?
};
let ids: Vec<i32> = list.iter().map(|p| p.id).collect();
let grouped = product_variants::Entity::grouped_for_products(&ctx.db, &ids).await?;
@@ -352,6 +359,7 @@ async fn index(
"audience": audience,
"category_groups": category_groups,
"selected_category": selected_category,
"query": query,
"total_count": list.len(),
"uncategorized_count": category_ids.iter().filter(|c| c.is_none()).count(),
"lang": current_lang(&jar),

View File

@@ -102,7 +102,7 @@ async fn run_search(
.all(&ctx.db)
.await?
} else {
products::Entity::search(&ctx.db, &q_trim, SEARCH_CAP).await?
products::Entity::search(&ctx.db, &q_trim, SEARCH_CAP, true).await?
};
// 2. Attach representative variant + resolved price to each (drop products

View File

@@ -37,10 +37,13 @@ impl Entity {
/// `f_unaccent`, so diacritics never matter. Results are ranked by full-text
/// rank, then trigram closeness of the name, then recency. An empty/blank
/// query returns nothing — callers fall back to the plain listing.
/// `published_only` filters to the storefront-visible set; pass `false` for
/// admin tools that also need to find drafts.
pub async fn search<C: ConnectionTrait>(
db: &C,
query: &str,
limit: u64,
published_only: bool,
) -> Result<Vec<Model>, DbErr> {
let q = query.trim();
if q.is_empty() {
@@ -50,12 +53,13 @@ impl Entity {
// Only the model's own columns are selected; the generated `search_vector`
// is left out so the row maps cleanly back onto `Model`. `$1` is reused
// for every occurrence of the query term; `$2` caps the result set.
let sql = r#"
let published_clause = if published_only { "p.published = TRUE AND" } else { "" };
let sql = format!(
r#"
SELECT p.created_at, p.updated_at, p.id, p.name, p.slug, p.description,
p.currency, p.view_count, p.published, p.published_at, p.category_id
FROM products p
WHERE p.published = TRUE
AND (
WHERE {published_clause} (
p.search_vector @@ websearch_to_tsquery('sk_unaccent', $1)
OR word_similarity(f_unaccent($1), f_unaccent(p.name)) > 0.3
OR word_similarity(f_unaccent($1), f_unaccent(COALESCE(p.description, ''))) > 0.3
@@ -65,12 +69,13 @@ impl Entity {
word_similarity(f_unaccent($1), f_unaccent(p.name)) DESC,
p.published_at DESC NULLS LAST
LIMIT $2
"#;
"#
);
Entity::find()
.from_raw_sql(Statement::from_sql_and_values(
db.get_database_backend(),
sql,
&sql,
[q.into(), (limit as i64).into()],
))
.all(db)