Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

11 changes: 10 additions & 1 deletion apps/labrinth/src/models/v3/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -928,8 +928,17 @@ impl VersionType {
}

#[derive(
Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, utoipa::ToSchema,
Serialize,
Deserialize,
Copy,
Clone,
Debug,
PartialEq,
Eq,
sqlx::Type,
utoipa::ToSchema,
)]
#[sqlx(type_name = "varchar", rename_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum DependencyType {
Required,
Expand Down
21 changes: 21 additions & 0 deletions apps/labrinth/src/search/backend/typesense/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,27 @@ impl SearchField {
sort: false,
optional: true,
},
SearchField::RequiredDependencies => TypesenseFieldSpec {
path: "required_dependencies",
ty: "string[]",
facet: true,
sort: false,
optional: true,
},
SearchField::OptionalDependencies => TypesenseFieldSpec {
path: "optional_dependencies",
ty: "string[]",
facet: true,
sort: false,
optional: true,
},
SearchField::Incompatibilities => TypesenseFieldSpec {
path: "incompatibilities",
ty: "string[]",
facet: true,
sort: false,
optional: true,
},
SearchField::Environment => TypesenseFieldSpec {
path: "environment",
ty: "string[]",
Expand Down
69 changes: 65 additions & 4 deletions apps/labrinth/src/search/indexing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ use crate::database::models::{
};
use crate::database::redis::RedisPool;
use crate::models::exp;
use crate::models::ids::ProjectId;
use crate::models::projects::from_duplicate_version_fields;
use crate::models::ids::{ProjectId, VersionId};
use crate::models::projects::{DependencyType, from_duplicate_version_fields};
use crate::models::v2::projects::LegacyProject;
use crate::routes::v2_reroute;
use crate::search::UploadSearchProject;
use crate::search::{Dependencies, Dependency, UploadSearchProject};
use crate::util::error::Context;

fn normalize_for_search(s: &str) -> String {
Expand Down Expand Up @@ -486,6 +486,7 @@ pub async fn index_local(
featured_gallery: featured_gallery.clone(),
open_source,
color: project.color.map(|x| x as u32),
dependencies: version.dependencies,
loader_fields,
project_loader_fields: project_loader_fields.clone(),
// 'loaders' is aggregate of all versions' loaders
Expand All @@ -507,6 +508,7 @@ struct PartialVersion {
project_types: Vec<String>,
version_fields: Vec<QueryVersionField>,
date_published: DateTime<Utc>,
dependencies: Dependencies,
}

async fn index_versions(
Expand Down Expand Up @@ -614,7 +616,60 @@ async fn index_versions(
.await
.wrap_err("failed to fetch version fields")?;

// Get version fields
// Get dependencies

// This can get a bit confusing to understand, so here's some documentation on it:
// pID = Project ID, vID = Version ID
//
// Projects:
// Project A (pID: 1) -> The Dependent
// Project B (pID: 2) -> The Dependency
//
// Versions:
// Project A (pID: 1) Version v1.0.0 (vID: 400)
// Project B (pID: 2) Version v2.0.0 (vID: 500)
//
// Returned Data:
// dependent_version_id (vID: 400) = Project A v1.0.0 (This is the version which is declaring a dependency aka The Dependent)
// dependency_project_id (pID: 2) = Project B (The Dependency) (nullable)
// dependency_version_id (vID: 500) = Project B v2.0.0 (The version required by the dependency) (nullable)
// dependency_type = "required"
//
// And the returned data ends up saying in plain words:
// Version (Project A v1.0.0, vID: 400) has a (required) dependency on the version (Project B v2.0.0, vID: 500) from the Project (Project B, pID: 2)
let dependencies: DashMap<DBVersionId, Dependencies> = sqlx::query!(
"
SELECT
d.dependent_id as \"dependent_version_id: DBVersionId\",
d.mod_dependency_id as \"dependency_project_id: DBProjectId\",
d.dependency_id as \"dependency_version_id: DBVersionId\",
d.dependency_type as \"dependency_type: DependencyType\"
FROM dependencies d
INNER JOIN mods m ON m.id = d.mod_dependency_id
WHERE dependent_id = ANY($1) AND dependency_type != 'embedded'
",
&all_version_ids
)
.fetch(pool)
.try_fold(
DashMap::new(),
|acc: DashMap<DBVersionId, Dependencies>, m| {
if let Some(dependency_project_id) = m.dependency_project_id {
let dependency = Dependency {
project: ProjectId::from(dependency_project_id),
version: m.dependency_version_id.map(VersionId::from),
};

acc.entry(m.dependent_version_id)
.or_default()
.add_dependency(m.dependency_type, dependency);
}

async move { Ok(acc) }
},
)
.await
.wrap_err("failed to fetch dependencies")?;

// Convert to partial versions
let mut res_versions: HashMap<DBProjectId, Vec<PartialVersion>> =
Expand All @@ -633,6 +688,11 @@ async fn index_versions(
.map(|(_, version_fields)| version_fields)
.unwrap_or_default();

let dependencies = dependencies
.remove(version_id)
.map(|(_, dependencies)| dependencies)
.unwrap_or_default();

res_versions
.entry(*project_id)
.or_default()
Expand All @@ -642,6 +702,7 @@ async fn index_versions(
project_types: version_loader_data.project_types,
version_fields,
date_published: *date_published,
dependencies,
});
}
}
Expand Down
Loading
Loading