From 345f314a1e7c1cf3da0bb80ba72e4b94140a86db Mon Sep 17 00:00:00 2001 From: aecsocket Date: Mon, 16 Mar 2026 16:44:29 +0000 Subject: [PATCH 1/4] Index search by original and split title --- .../src/search/backend/typesense/mod.rs | 21 ++++++++++++------- apps/labrinth/src/search/indexing.rs | 2 +- apps/labrinth/src/search/mod.rs | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/labrinth/src/search/backend/typesense/mod.rs b/apps/labrinth/src/search/backend/typesense/mod.rs index 026b940b76..dec7923e2c 100644 --- a/apps/labrinth/src/search/backend/typesense/mod.rs +++ b/apps/labrinth/src/search/backend/typesense/mod.rs @@ -105,18 +105,25 @@ impl Default for RequestConfig { } fn default_query_by() -> Vec { - ["indexed_title", "slug", "summary", "indexed_author"] - .into_iter() - .map(str::to_string) - .collect() + [ + "name", + "indexed_name", + "slug", + "author", + "indexed_author", + "summary", + ] + .into_iter() + .map(str::to_string) + .collect() } fn default_query_by_weights() -> Vec { - vec![15, 5, 2, 1] + vec![15, 15, 10, 3, 3, 1] } fn default_prefix() -> Vec { - vec![true, true, true, true] + vec![true, true, true, true, true, true] } const fn default_prioritize_exact_match() -> bool { @@ -491,7 +498,7 @@ impl Typesense { let mut fields = vec![ json!({"name": "summary", "type": "string", "facet": false}), json!({"name": "slug", "type": "string", "facet": false}), - json!({"name": "indexed_title", "type": "string", "facet": false, "stem": true}), + json!({"name": "indexed_name", "type": "string", "facet": false, "stem": true}), json!({"name": "indexed_author", "type": "string", "facet": false}), json!({"name": "log_downloads", "type": "float", "sort": true}), json!({"name": "follows", "type": "int32", "facet": true, "sort": true}), diff --git a/apps/labrinth/src/search/indexing.rs b/apps/labrinth/src/search/indexing.rs index cf4a92f931..a150c0eba6 100644 --- a/apps/labrinth/src/search/indexing.rs +++ b/apps/labrinth/src/search/indexing.rs @@ -427,7 +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(), + indexed_name: project.name.to_kebab_case(), summary: project.summary.clone(), categories: categories.clone(), display_categories: display_categories.clone(), diff --git a/apps/labrinth/src/search/mod.rs b/apps/labrinth/src/search/mod.rs index 545a0c4fc6..843a6519ac 100644 --- a/apps/labrinth/src/search/mod.rs +++ b/apps/labrinth/src/search/mod.rs @@ -230,7 +230,7 @@ pub struct UploadSearchProject { pub author: String, pub indexed_author: String, pub name: String, - pub indexed_title: String, + pub indexed_name: String, pub summary: String, pub categories: Vec, pub display_categories: Vec, From 15276a43b1a497fd9132f25721a9a8c9717a1db7 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Tue, 17 Mar 2026 16:08:04 +0000 Subject: [PATCH 2/4] better normalization of title/author names for indexing --- apps/labrinth/src/search/indexing.rs | 41 ++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/apps/labrinth/src/search/indexing.rs b/apps/labrinth/src/search/indexing.rs index a150c0eba6..4ded532065 100644 --- a/apps/labrinth/src/search/indexing.rs +++ b/apps/labrinth/src/search/indexing.rs @@ -4,7 +4,9 @@ use eyre::Result; use futures::TryStreamExt; use heck::ToKebabCase; use itertools::Itertools; +use regex::Regex; use std::collections::HashMap; +use std::sync::LazyLock; use tracing::info; use crate::database::PgPool; @@ -25,6 +27,13 @@ use crate::routes::v2_reroute; use crate::search::UploadSearchProject; use crate::util::error::Context; +fn normalize_for_search(s: &str) -> String { + static SPECIAL_CHARS_RE: LazyLock = + LazyLock::new(|| Regex::new(r"[^a-zA-Z0-9-\s]").expect("valid regex")); + + SPECIAL_CHARS_RE.replace_all(s, "").to_kebab_case() +} + pub async fn index_local( pool: &PgPool, redis: &RedisPool, @@ -427,7 +436,7 @@ pub async fn index_local( project_id: crate::models::ids::ProjectId::from(project.id) .to_string(), name: project.name.clone(), - indexed_name: project.name.to_kebab_case(), + indexed_name: normalize_for_search(&project.name), summary: project.summary.clone(), categories: categories.clone(), display_categories: display_categories.clone(), @@ -436,7 +445,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(), + indexed_author: normalize_for_search(&owner), date_created: project.approved, created_timestamp: project.approved.timestamp(), date_modified: project.updated, @@ -614,3 +623,31 @@ async fn index_versions( Ok(res_versions) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_normalize_for_search_removes_special_chars() { + assert_eq!(normalize_for_search("Xaero's Minimap"), "xaeros-minimap"); + assert_eq!(normalize_for_search("JourneyMap"), "journey-map"); + assert_eq!(normalize_for_search("journey-map"), "journey-map"); + assert_eq!(normalize_for_search("SomeUserName"), "some-user-name"); + } + + #[test] + fn test_normalize_for_search_handles_whitespace() { + assert_eq!( + normalize_for_search("Some Project Name"), + "some-project-name" + ); + assert_eq!(normalize_for_search(" padded "), "padded"); + } + + #[test] + fn test_normalize_for_search_handles_numbers() { + assert_eq!(normalize_for_search("Project 123"), "project-123"); + assert_eq!(normalize_for_search("Test 1.0"), "test-1-0"); + } +} From 0a872ca9bdb9b6fe670c945a9bab5ec6e0f7a250 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Wed, 18 Mar 2026 18:31:23 +0000 Subject: [PATCH 3/4] replace println with warn --- apps/labrinth/src/search/indexing.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/labrinth/src/search/indexing.rs b/apps/labrinth/src/search/indexing.rs index 4ded532065..d092057459 100644 --- a/apps/labrinth/src/search/indexing.rs +++ b/apps/labrinth/src/search/indexing.rs @@ -7,7 +7,7 @@ use itertools::Itertools; use regex::Regex; use std::collections::HashMap; use std::sync::LazyLock; -use tracing::info; +use tracing::{info, warn}; use crate::database::PgPool; use crate::database::models::loader_fields::{ @@ -271,7 +271,7 @@ pub async fn index_local( { team_owner } else { - println!( + warn!( "org owner not found for project {} id: {}!", project.name, project.id.0 ); From cd04f7d80104944ad0ebf90ae1ba2bdbf86c2bee Mon Sep 17 00:00:00 2001 From: aecsocket Date: Fri, 20 Mar 2026 00:23:04 +0000 Subject: [PATCH 4/4] fix test --- apps/labrinth/src/search/indexing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/labrinth/src/search/indexing.rs b/apps/labrinth/src/search/indexing.rs index d092057459..b081ce9aae 100644 --- a/apps/labrinth/src/search/indexing.rs +++ b/apps/labrinth/src/search/indexing.rs @@ -29,7 +29,7 @@ use crate::util::error::Context; fn normalize_for_search(s: &str) -> String { static SPECIAL_CHARS_RE: LazyLock = - LazyLock::new(|| Regex::new(r"[^a-zA-Z0-9-\s]").expect("valid regex")); + LazyLock::new(|| Regex::new(r"[^a-zA-Z0-9-.\s]").expect("valid regex")); SPECIAL_CHARS_RE.replace_all(s, "").to_kebab_case() }