From 7c9d6f29a2814a216483d44ee1a0c8345e1126d7 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Thu, 11 Jun 2026 20:33:17 +0100 Subject: [PATCH] include compatible dependency projects field --- ...9056ac842c402bd0941c45ebd1dc6d627d43.json} | 14 +++- .../src/search/backend/typesense/mod.rs | 7 ++ apps/labrinth/src/search/indexing.rs | 75 ++++++++++++------- apps/labrinth/src/search/mod.rs | 8 ++ apps/labrinth/tests/search.rs | 18 +++++ 5 files changed, 89 insertions(+), 33 deletions(-) rename apps/labrinth/.sqlx/{query-20cb8a3d45911a126aa17451b1e6e84e9238dee422cc2b400cea0048a71e4c27.json => query-3afdd6b9070ea0951682facf207b9056ac842c402bd0941c45ebd1dc6d627d43.json} (53%) diff --git a/apps/labrinth/.sqlx/query-20cb8a3d45911a126aa17451b1e6e84e9238dee422cc2b400cea0048a71e4c27.json b/apps/labrinth/.sqlx/query-3afdd6b9070ea0951682facf207b9056ac842c402bd0941c45ebd1dc6d627d43.json similarity index 53% rename from apps/labrinth/.sqlx/query-20cb8a3d45911a126aa17451b1e6e84e9238dee422cc2b400cea0048a71e4c27.json rename to apps/labrinth/.sqlx/query-3afdd6b9070ea0951682facf207b9056ac842c402bd0941c45ebd1dc6d627d43.json index 5d7941bfe8..694308e564 100644 --- a/apps/labrinth/.sqlx/query-20cb8a3d45911a126aa17451b1e6e84e9238dee422cc2b400cea0048a71e4c27.json +++ b/apps/labrinth/.sqlx/query-3afdd6b9070ea0951682facf207b9056ac842c402bd0941c45ebd1dc6d627d43.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT DISTINCT v.mod_id dependent_project_id, d.mod_dependency_id dependency_project_id,\n m.name dependency_name, m.slug dependency_slug, m.icon_url dependency_icon_url\n FROM versions v\n INNER JOIN dependencies d ON d.dependent_id = v.id\n INNER JOIN mods m ON m.id = d.mod_dependency_id\n WHERE v.mod_id = ANY($1)\n AND d.mod_dependency_id IS NOT NULL\n AND m.status = ANY($2)\n ", + "query": "\n SELECT DISTINCT v.mod_id dependent_project_id,\n d.mod_dependency_id dependency_project_id,\n d.dependency_type dependency_type,\n m.name dependency_name,\n m.slug dependency_slug,\n m.icon_url dependency_icon_url\n FROM versions v\n INNER JOIN dependencies d ON d.dependent_id = v.id\n INNER JOIN mods m ON m.id = d.mod_dependency_id\n WHERE v.mod_id = ANY($1)\n AND d.mod_dependency_id IS NOT NULL\n AND m.status = ANY($2)\n ", "describe": { "columns": [ { @@ -15,16 +15,21 @@ }, { "ordinal": 2, - "name": "dependency_name", + "name": "dependency_type", "type_info": "Varchar" }, { "ordinal": 3, - "name": "dependency_slug", + "name": "dependency_name", "type_info": "Varchar" }, { "ordinal": 4, + "name": "dependency_slug", + "type_info": "Varchar" + }, + { + "ordinal": 5, "name": "dependency_icon_url", "type_info": "Varchar" } @@ -39,9 +44,10 @@ false, true, false, + false, true, true ] }, - "hash": "20cb8a3d45911a126aa17451b1e6e84e9238dee422cc2b400cea0048a71e4c27" + "hash": "3afdd6b9070ea0951682facf207b9056ac842c402bd0941c45ebd1dc6d627d43" } diff --git a/apps/labrinth/src/search/backend/typesense/mod.rs b/apps/labrinth/src/search/backend/typesense/mod.rs index 5959d95789..7504587143 100644 --- a/apps/labrinth/src/search/backend/typesense/mod.rs +++ b/apps/labrinth/src/search/backend/typesense/mod.rs @@ -485,6 +485,13 @@ impl SearchField { sort: false, optional: true, }, + SearchField::CompatibleDependencyProjectIds => TypesenseFieldSpec { + path: "compatible_dependency_project_ids", + ty: "string[]", + facet: true, + sort: false, + optional: true, + }, } } } diff --git a/apps/labrinth/src/search/indexing.rs b/apps/labrinth/src/search/indexing.rs index 314f841f92..65d2929a9f 100644 --- a/apps/labrinth/src/search/indexing.rs +++ b/apps/labrinth/src/search/indexing.rs @@ -124,10 +124,14 @@ pub async fn index_local( info!("Indexing local dependencies!"); let dependencies: DashMap> = - sqlx::query!( - " - SELECT DISTINCT v.mod_id dependent_project_id, d.mod_dependency_id dependency_project_id, - m.name dependency_name, m.slug dependency_slug, m.icon_url dependency_icon_url + sqlx::query!( + " + SELECT DISTINCT v.mod_id dependent_project_id, + d.mod_dependency_id dependency_project_id, + d.dependency_type dependency_type, + m.name dependency_name, + m.slug dependency_slug, + m.icon_url dependency_icon_url FROM versions v INNER JOIN dependencies d ON d.dependent_id = v.id INNER JOIN mods m ON m.id = d.mod_dependency_id @@ -135,32 +139,33 @@ pub async fn index_local( AND d.mod_dependency_id IS NOT NULL AND m.status = ANY($2) ", - &project_ids, - &searchable_statuses, - ) - .fetch(pool) - .try_fold( - DashMap::new(), - |acc: DashMap>, m| { - if let Some(dependency_project_id) = m.dependency_project_id { - acc.entry(DBProjectId(m.dependent_project_id)) - .or_default() - .push(SearchProjectDependency { - project_id: ProjectId::from(DBProjectId( - dependency_project_id, - )) - .to_string(), - name: m.dependency_name, - slug: m.dependency_slug, - icon_url: m.dependency_icon_url, - }); - } + &project_ids, + &searchable_statuses, + ) + .fetch(pool) + .try_fold( + DashMap::new(), + |acc: DashMap>, m| { + if let Some(dependency_project_id) = m.dependency_project_id { + acc.entry(DBProjectId(m.dependent_project_id)) + .or_default() + .push(SearchProjectDependency { + project_id: ProjectId::from(DBProjectId( + dependency_project_id, + )) + .to_string(), + dependency_type: m.dependency_type, + name: m.dependency_name, + slug: m.dependency_slug, + icon_url: m.dependency_icon_url, + }); + } - async move { Ok(acc) } - }, - ) - .await - .wrap_err("failed to fetch project dependencies")?; + async move { Ok(acc) } + }, + ) + .await + .wrap_err("failed to fetch project dependencies")?; struct PartialGallery { url: String, @@ -398,6 +403,16 @@ pub async fn index_local( .iter() .map(|dependency| dependency.project_id.clone()) .collect::>(); + let compatible_dependency_project_ids = dependencies + .iter() + .filter(|dependency| { + matches!( + dependency.dependency_type.as_str(), + "required" | "optional" + ) + }) + .map(|dependency| dependency.project_id.clone()) + .collect::>(); if let Some(versions) = versions.remove(&project.id) { // Aggregated project loader fields @@ -539,6 +554,8 @@ pub async fn index_local( open_source, color: project.color.map(|x| x as u32), dependency_project_ids: dependency_project_ids.clone(), + compatible_dependency_project_ids: + compatible_dependency_project_ids.clone(), dependencies: dependencies.clone(), loader_fields, project_loader_fields: project_loader_fields.clone(), diff --git a/apps/labrinth/src/search/mod.rs b/apps/labrinth/src/search/mod.rs index 90c0f9e3cd..6f3518438e 100644 --- a/apps/labrinth/src/search/mod.rs +++ b/apps/labrinth/src/search/mod.rs @@ -196,6 +196,7 @@ pub enum SearchField { MinecraftJavaServerContentSupportedGameVersions, MinecraftJavaServerPingData, DependencyProjectIds, + CompatibleDependencyProjectIds, } #[derive(Debug, Error)] @@ -252,6 +253,8 @@ pub struct UploadSearchProject { #[serde(default)] pub dependency_project_ids: Vec, #[serde(default)] + pub compatible_dependency_project_ids: Vec, + #[serde(default)] pub dependencies: Vec, // Hidden fields to get the Project model out of the search results. @@ -267,6 +270,7 @@ pub struct UploadSearchProject { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SearchProjectDependency { pub project_id: String, + pub dependency_type: String, pub name: String, pub slug: Option, pub icon_url: Option, @@ -311,6 +315,8 @@ pub struct ResultSearchProject { #[serde(default)] pub dependency_project_ids: Vec, #[serde(default)] + pub compatible_dependency_project_ids: Vec, + #[serde(default)] pub dependencies: Vec, // Hidden fields to get the Project model out of the search results. @@ -350,6 +356,8 @@ impl From for ResultSearchProject { featured_gallery: source.featured_gallery, color: source.color, dependency_project_ids: source.dependency_project_ids, + compatible_dependency_project_ids: source + .compatible_dependency_project_ids, dependencies: source.dependencies, loaders: source.loaders, project_loader_fields: source.project_loader_fields, diff --git a/apps/labrinth/tests/search.rs b/apps/labrinth/tests/search.rs index 9bcba177c8..a7c25a34bf 100644 --- a/apps/labrinth/tests/search.rs +++ b/apps/labrinth/tests/search.rs @@ -95,6 +95,12 @@ async fn search_projects() { )]]), vec![7], ), + ( + json!([[format!( + "compatible_dependency_project_ids:{dependency_project_id}" + )]]), + vec![7], + ), ]; // TODO: versions, game versions // Untested: @@ -151,11 +157,23 @@ async fn search_projects() { projects.hits[0].dependency_project_ids[0], dependency_project_id ); + assert_eq!( + projects.hits[0].compatible_dependency_project_ids.len(), + 1 + ); + assert_eq!( + projects.hits[0].compatible_dependency_project_ids[0], + dependency_project_id + ); assert_eq!(projects.hits[0].dependencies.len(), 1); assert_eq!( projects.hits[0].dependencies[0].project_id, dependency_project_id ); + assert_eq!( + projects.hits[0].dependencies[0].dependency_type, + "required" + ); assert!( projects.hits[0].dependencies[0] .slug