From 991b4d8c1326e4f196cd273cbf2a0d4c955e781a Mon Sep 17 00:00:00 2001 From: aecsocket Date: Fri, 13 Mar 2026 13:34:51 +0000 Subject: [PATCH] Fix Typesense tests (#5541) * Fix Typesense tests * fix * add back author * Split project title and authors by words and index on that * clippy --- Cargo.lock | 1 + apps/app/src/api/profile.rs | 2 +- apps/labrinth/Cargo.toml | 1 + .../src/search/backend/elasticsearch/mod.rs | 15 ++++++++ .../search/backend/meilisearch/indexing.rs | 12 +++++++ .../src/search/backend/typesense/mod.rs | 35 +++++++++++++++---- apps/labrinth/src/search/indexing.rs | 3 ++ apps/labrinth/src/search/mod.rs | 5 +++ 8 files changed, 66 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9171376d1..639c608a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4922,6 +4922,7 @@ dependencies = [ "eyre", "futures", "futures-util", + "heck 0.5.0", "hex", "hmac", "hyper-rustls 0.27.7", diff --git a/apps/app/src/api/profile.rs b/apps/app/src/api/profile.rs index cc60df34f..3d08f437f 100644 --- a/apps/app/src/api/profile.rs +++ b/apps/app/src/api/profile.rs @@ -208,7 +208,7 @@ pub async fn profile_check_installed_batch( projects.into_iter().any(|(_, pf)| { pf.metadata .as_ref() - .map_or(false, |m| m.project_id == project_id) + .is_some_and(|m| m.project_id == project_id) }) } else { false diff --git a/apps/labrinth/Cargo.toml b/apps/labrinth/Cargo.toml index 0e9c8605b..d977d57c6 100644 --- a/apps/labrinth/Cargo.toml +++ b/apps/labrinth/Cargo.toml @@ -48,6 +48,7 @@ elasticsearch = { workspace = true, features = ["experimental-apis"] } eyre = { workspace = true } futures = { workspace = true } futures-util = { workspace = true } +heck = { workspace = true } hex = { workspace = true } hmac = { workspace = true } hyper-rustls = { workspace = true } diff --git a/apps/labrinth/src/search/backend/elasticsearch/mod.rs b/apps/labrinth/src/search/backend/elasticsearch/mod.rs index 53600013d..5c8cb8612 100644 --- a/apps/labrinth/src/search/backend/elasticsearch/mod.rs +++ b/apps/labrinth/src/search/backend/elasticsearch/mod.rs @@ -136,6 +136,21 @@ impl SearchField { path: "categories", mapping: json!({ "type": "keyword" }), }, + SearchField::Name => ElasticsearchFieldSpec { + path: "name", + mapping: json!({ "type": "search_as_you_type" }), + }, + SearchField::Author => ElasticsearchFieldSpec { + path: "author", + mapping: json!({ + "type": "search_as_you_type", + "fields": { "keyword": { "type": "keyword" } } + }), + }, + SearchField::License => ElasticsearchFieldSpec { + path: "license", + mapping: json!({ "type": "keyword" }), + }, SearchField::ProjectTypes => ElasticsearchFieldSpec { path: "project_types", mapping: json!({ "type": "keyword" }), diff --git a/apps/labrinth/src/search/backend/meilisearch/indexing.rs b/apps/labrinth/src/search/backend/meilisearch/indexing.rs index fa10b1522..8f2a4bfa7 100644 --- a/apps/labrinth/src/search/backend/meilisearch/indexing.rs +++ b/apps/labrinth/src/search/backend/meilisearch/indexing.rs @@ -557,6 +557,18 @@ impl SearchField { path: "categories", filterable: true, }, + SearchField::Name => MeilisearchFieldSpec { + path: "name", + filterable: true, + }, + SearchField::Author => MeilisearchFieldSpec { + path: "author", + filterable: true, + }, + SearchField::License => MeilisearchFieldSpec { + path: "license", + filterable: true, + }, SearchField::ProjectTypes => MeilisearchFieldSpec { path: "project_types", filterable: true, diff --git a/apps/labrinth/src/search/backend/typesense/mod.rs b/apps/labrinth/src/search/backend/typesense/mod.rs index 527eafba1..7408ed4e4 100644 --- a/apps/labrinth/src/search/backend/typesense/mod.rs +++ b/apps/labrinth/src/search/backend/typesense/mod.rs @@ -105,18 +105,18 @@ impl Default for RequestConfig { } fn default_query_by() -> Vec { - ["name", "slug", "summary"] + ["indexed_title", "slug", "summary", "indexed_author"] .into_iter() .map(str::to_string) .collect() } fn default_query_by_weights() -> Vec { - vec![15, 5, 2] + vec![15, 5, 2, 1] } fn default_prefix() -> Vec { - vec![true, true, false] + vec![true, true, true, true] } const fn default_prioritize_exact_match() -> bool { @@ -346,6 +346,27 @@ impl SearchField { sort: false, optional: true, }, + SearchField::Name => TypesenseFieldSpec { + path: "name", + ty: "string", + facet: true, + sort: false, + optional: false, + }, + SearchField::Author => TypesenseFieldSpec { + path: "author", + ty: "string", + facet: true, + sort: false, + optional: false, + }, + SearchField::License => TypesenseFieldSpec { + path: "license", + ty: "string", + facet: true, + sort: false, + optional: true, + }, SearchField::ProjectTypes => TypesenseFieldSpec { path: "project_types", ty: "string[]", @@ -468,12 +489,11 @@ impl Typesense { fn collection_schema(name: &str) -> Value { let mut fields = vec![ - json!({"name": "name", "type": "string", "facet": false}), json!({"name": "summary", "type": "string", "facet": false}), json!({"name": "slug", "type": "string", "facet": false}), + json!({"name": "indexed_title", "type": "string", "facet": false}), + json!({"name": "indexed_author", "type": "string", "facet": false}), json!({"name": "log_downloads", "type": "float", "sort": true}), - json!({"name": "author", "type": "string", "facet": true}), - json!({"name": "license", "type": "string", "facet": true}), json!({"name": "follows", "type": "int32", "facet": true, "sort": true}), json!({"name": "created_timestamp", "type": "int64", "sort": true}), json!({"name": "modified_timestamp", "type": "int64", "sort": true}), @@ -487,6 +507,7 @@ impl Typesense { json!({ "name": name, "enable_nested_fields": true, + "token_separators": ["-"], "fields": fields, "default_sorting_field": "log_downloads" }) @@ -596,7 +617,7 @@ impl Typesense { }; let new_filters_part = - info.new_filters.as_deref().map(|f| meili_to_typesense(f)); + info.new_filters.as_deref().map(meili_to_typesense); let legacy_part = if info.new_filters.is_none() { combined_search_filters(info).map(|f| meili_to_typesense(&f)) diff --git a/apps/labrinth/src/search/indexing.rs b/apps/labrinth/src/search/indexing.rs index 1b8bb8e79..cf4a92f93 100644 --- a/apps/labrinth/src/search/indexing.rs +++ b/apps/labrinth/src/search/indexing.rs @@ -2,6 +2,7 @@ use chrono::{DateTime, Utc}; use dashmap::DashMap; use eyre::Result; use futures::TryStreamExt; +use heck::ToKebabCase; use itertools::Itertools; use std::collections::HashMap; use tracing::info; @@ -426,6 +427,7 @@ pub async fn index_local( project_id: crate::models::ids::ProjectId::from(project.id) .to_string(), name: project.name.clone(), + indexed_title: project.name.to_kebab_case(), summary: project.summary.clone(), categories: categories.clone(), display_categories: display_categories.clone(), @@ -434,6 +436,7 @@ pub async fn index_local( log_downloads: (project.downloads.max(1) as f64).ln(), icon_url: project.icon_url.clone(), author: owner.clone(), + indexed_author: owner.to_kebab_case(), date_created: project.approved, created_timestamp: project.approved.timestamp(), date_modified: project.updated, diff --git a/apps/labrinth/src/search/mod.rs b/apps/labrinth/src/search/mod.rs index 97bbd92b0..545a0c4fc 100644 --- a/apps/labrinth/src/search/mod.rs +++ b/apps/labrinth/src/search/mod.rs @@ -186,6 +186,9 @@ pub enum SearchBackendKind { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::EnumIter)] pub enum SearchField { Categories, + Name, + Author, + License, ProjectTypes, ProjectId, OpenSource, @@ -225,7 +228,9 @@ pub struct UploadSearchProject { pub project_types: Vec, pub slug: Option, pub author: String, + pub indexed_author: String, pub name: String, + pub indexed_title: String, pub summary: String, pub categories: Vec, pub display_categories: Vec,