You've already forked AstralRinth
forked from didirus/AstralRinth
Project Types, Code Cleanup, and Rename Mods -> Projects (#192)
* Initial work for modpacks and project types * Code cleanup, fix some issues * Username route getting, remove pointless tests * Base validator types + fixes * Fix strange IML generation * Multiple hash requests for version files * Fix docker build (hopefully) * Legacy routes * Finish validator architecture * Update rust version in dockerfile * Added caching and fixed typo (#203) * Added caching and fixed typo * Fixed clippy error * Removed log for cache * Add final validators, fix how loaders are handled and add icons to tags * Fix search module * Fix parts of legacy API not working Co-authored-by: Redblueflame <contact@redblueflame.com>
This commit is contained in:
@@ -2,32 +2,32 @@ use futures::{StreamExt, TryStreamExt};
|
||||
use log::info;
|
||||
|
||||
use super::IndexingError;
|
||||
use crate::models::mods::SideType;
|
||||
use crate::search::UploadSearchMod;
|
||||
use crate::models::projects::SideType;
|
||||
use crate::search::UploadSearchProject;
|
||||
use sqlx::postgres::PgPool;
|
||||
use std::borrow::Cow;
|
||||
|
||||
// TODO: only loaders for recent versions? For mods that have moved from forge to fabric
|
||||
pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingError> {
|
||||
info!("Indexing local mods!");
|
||||
// TODO: only loaders for recent versions? For projects that have moved from forge to fabric
|
||||
pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchProject>, IndexingError> {
|
||||
info!("Indexing local projects!");
|
||||
|
||||
let mut docs_to_add: Vec<UploadSearchMod> = vec![];
|
||||
let mut docs_to_add: Vec<UploadSearchProject> = vec![];
|
||||
|
||||
let mut mods = sqlx::query!(
|
||||
let mut projects = sqlx::query!(
|
||||
"
|
||||
SELECT m.id, m.title, m.description, m.downloads, m.follows, m.icon_url, m.body_url, m.published, m.updated, m.team_id, m.status, m.slug, m.license, m.client_side, m.server_side FROM mods m
|
||||
"
|
||||
).fetch(&pool);
|
||||
|
||||
while let Some(result) = mods.next().await {
|
||||
if let Ok(mod_data) = result {
|
||||
let status = crate::models::mods::ModStatus::from_str(
|
||||
while let Some(result) = projects.next().await {
|
||||
if let Ok(project_data) = result {
|
||||
let status = crate::models::projects::ProjectStatus::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT status FROM statuses
|
||||
WHERE id = $1
|
||||
",
|
||||
mod_data.status,
|
||||
SELECT status FROM statuses
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.status,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
@@ -46,7 +46,7 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
WHERE versions.mod_id = $1
|
||||
ORDER BY gv.created ASC
|
||||
",
|
||||
mod_data.id
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| c.version)) })
|
||||
@@ -60,7 +60,7 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
INNER JOIN loaders ON loaders.id = lv.loader_id
|
||||
WHERE versions.mod_id = $1
|
||||
",
|
||||
mod_data.id
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| Cow::Owned(c.loader))) })
|
||||
@@ -74,7 +74,7 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
INNER JOIN categories c ON mc.joining_category_id=c.id
|
||||
WHERE mc.joining_mod_id = $1
|
||||
",
|
||||
mod_data.id
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| Cow::Owned(c.category))) })
|
||||
@@ -90,22 +90,21 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
WHERE tm.team_id = $2 AND tm.role = $1
|
||||
",
|
||||
crate::models::teams::OWNER_ROLE,
|
||||
mod_data.team_id,
|
||||
project_data.team_id,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
let mut icon_url = "".to_string();
|
||||
|
||||
if let Some(url) = mod_data.icon_url {
|
||||
if let Some(url) = project_data.icon_url {
|
||||
icon_url = url;
|
||||
}
|
||||
|
||||
let mod_id = crate::models::ids::ModId(mod_data.id as u64);
|
||||
let author_id = crate::models::ids::UserId(user.id as u64);
|
||||
let project_id = crate::models::ids::ProjectId(project_data.id as u64);
|
||||
|
||||
// TODO: is this correct? This just gets the latest version of
|
||||
// minecraft that this mod has a version that supports; it doesn't
|
||||
// minecraft that this project has a version that supports; it doesn't
|
||||
// take betas or other info into account.
|
||||
let latest_version = versions
|
||||
.last()
|
||||
@@ -116,10 +115,10 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
let client_side = SideType::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
mod_data.client_side,
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.client_side,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
@@ -129,10 +128,10 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
let server_side = SideType::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
mod_data.server_side,
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.server_side,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
@@ -140,33 +139,31 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
);
|
||||
|
||||
let license = crate::database::models::categories::License::get(
|
||||
crate::database::models::LicenseId(mod_data.license),
|
||||
crate::database::models::LicenseId(project_data.license),
|
||||
&pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
docs_to_add.push(UploadSearchMod {
|
||||
mod_id: format!("local-{}", mod_id),
|
||||
title: mod_data.title,
|
||||
description: mod_data.description,
|
||||
docs_to_add.push(UploadSearchProject {
|
||||
project_id: format!("local-{}", project_id),
|
||||
title: project_data.title,
|
||||
description: project_data.description,
|
||||
categories,
|
||||
versions,
|
||||
follows: mod_data.follows,
|
||||
downloads: mod_data.downloads,
|
||||
page_url: format!("https://modrinth.com/mod/{}", mod_id),
|
||||
follows: project_data.follows,
|
||||
downloads: project_data.downloads,
|
||||
icon_url,
|
||||
author: user.username,
|
||||
author_url: format!("https://modrinth.com/user/{}", author_id),
|
||||
date_created: mod_data.published,
|
||||
created_timestamp: mod_data.published.timestamp(),
|
||||
date_modified: mod_data.updated,
|
||||
modified_timestamp: mod_data.updated.timestamp(),
|
||||
date_created: project_data.published,
|
||||
created_timestamp: project_data.published.timestamp(),
|
||||
date_modified: project_data.updated,
|
||||
modified_timestamp: project_data.updated.timestamp(),
|
||||
latest_version,
|
||||
license: license.short,
|
||||
client_side: client_side.to_string(),
|
||||
server_side: server_side.to_string(),
|
||||
host: Cow::Borrowed("modrinth"),
|
||||
slug: mod_data.slug,
|
||||
slug: project_data.slug,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -175,10 +172,10 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
}
|
||||
|
||||
pub async fn query_one(
|
||||
id: crate::database::models::ModId,
|
||||
id: crate::database::models::ProjectId,
|
||||
exec: &mut sqlx::PgConnection,
|
||||
) -> Result<UploadSearchMod, IndexingError> {
|
||||
let mod_data = sqlx::query!(
|
||||
) -> Result<UploadSearchProject, IndexingError> {
|
||||
let project_data = sqlx::query!(
|
||||
"
|
||||
SELECT m.id, m.title, m.description, m.downloads, m.follows, m.icon_url, m.body_url, m.published, m.updated, m.team_id, m.slug, m.license, m.client_side, m.server_side
|
||||
FROM mods m
|
||||
@@ -195,7 +192,7 @@ pub async fn query_one(
|
||||
WHERE versions.mod_id = $1
|
||||
ORDER BY gv.created ASC
|
||||
",
|
||||
mod_data.id
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&mut *exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| c.version)) })
|
||||
@@ -209,7 +206,7 @@ pub async fn query_one(
|
||||
INNER JOIN loaders ON loaders.id = lv.loader_id
|
||||
WHERE versions.mod_id = $1
|
||||
",
|
||||
mod_data.id
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&mut *exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| Cow::Owned(c.loader))) })
|
||||
@@ -223,7 +220,7 @@ pub async fn query_one(
|
||||
INNER JOIN categories c ON mc.joining_category_id=c.id
|
||||
WHERE mc.joining_mod_id = $1
|
||||
",
|
||||
mod_data.id
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&mut *exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| Cow::Owned(c.category))) })
|
||||
@@ -239,22 +236,21 @@ pub async fn query_one(
|
||||
WHERE tm.team_id = $2 AND tm.role = $1
|
||||
",
|
||||
crate::models::teams::OWNER_ROLE,
|
||||
mod_data.team_id,
|
||||
project_data.team_id,
|
||||
)
|
||||
.fetch_one(&mut *exec)
|
||||
.await?;
|
||||
|
||||
let mut icon_url = "".to_string();
|
||||
|
||||
if let Some(url) = mod_data.icon_url {
|
||||
if let Some(url) = project_data.icon_url {
|
||||
icon_url = url;
|
||||
}
|
||||
|
||||
let mod_id = crate::models::ids::ModId(mod_data.id as u64);
|
||||
let author_id = crate::models::ids::UserId(user.id as u64);
|
||||
let project_id = crate::models::ids::ProjectId(project_data.id as u64);
|
||||
|
||||
// TODO: is this correct? This just gets the latest version of
|
||||
// minecraft that this mod has a version that supports; it doesn't
|
||||
// minecraft that this project has a version that supports; it doesn't
|
||||
// take betas or other info into account.
|
||||
let latest_version = versions
|
||||
.last()
|
||||
@@ -265,10 +261,10 @@ pub async fn query_one(
|
||||
let client_side = SideType::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
mod_data.client_side,
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.client_side,
|
||||
)
|
||||
.fetch_one(&mut *exec)
|
||||
.await?
|
||||
@@ -278,10 +274,10 @@ pub async fn query_one(
|
||||
let server_side = SideType::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
mod_data.server_side,
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.server_side,
|
||||
)
|
||||
.fetch_one(&mut *exec)
|
||||
.await?
|
||||
@@ -289,32 +285,30 @@ pub async fn query_one(
|
||||
);
|
||||
|
||||
let license = crate::database::models::categories::License::get(
|
||||
crate::database::models::LicenseId(mod_data.license),
|
||||
crate::database::models::LicenseId(project_data.license),
|
||||
&mut *exec,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(UploadSearchMod {
|
||||
mod_id: format!("local-{}", mod_id),
|
||||
title: mod_data.title,
|
||||
description: mod_data.description,
|
||||
Ok(UploadSearchProject {
|
||||
project_id: format!("local-{}", project_id),
|
||||
title: project_data.title,
|
||||
description: project_data.description,
|
||||
categories,
|
||||
versions,
|
||||
follows: mod_data.follows,
|
||||
downloads: mod_data.downloads,
|
||||
page_url: format!("https://modrinth.com/mod/{}", mod_id),
|
||||
follows: project_data.follows,
|
||||
downloads: project_data.downloads,
|
||||
icon_url,
|
||||
author: user.username,
|
||||
author_url: format!("https://modrinth.com/user/{}", author_id),
|
||||
date_created: mod_data.published,
|
||||
created_timestamp: mod_data.published.timestamp(),
|
||||
date_modified: mod_data.updated,
|
||||
modified_timestamp: mod_data.updated.timestamp(),
|
||||
date_created: project_data.published,
|
||||
created_timestamp: project_data.published.timestamp(),
|
||||
date_modified: project_data.updated,
|
||||
modified_timestamp: project_data.updated.timestamp(),
|
||||
latest_version,
|
||||
license: license.short,
|
||||
client_side: client_side.to_string(),
|
||||
server_side: server_side.to_string(),
|
||||
host: Cow::Borrowed("modrinth"),
|
||||
slug: mod_data.slug,
|
||||
slug: project_data.slug,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
pub mod local_import;
|
||||
pub mod queue;
|
||||
|
||||
use crate::search::{SearchConfig, UploadSearchMod};
|
||||
use crate::search::{SearchConfig, UploadSearchProject};
|
||||
use local_import::index_local;
|
||||
use meilisearch_sdk::client::Client;
|
||||
use meilisearch_sdk::indexes::Index;
|
||||
@@ -27,14 +27,13 @@ pub enum IndexingError {
|
||||
EnvError(#[from] dotenv::Error),
|
||||
}
|
||||
|
||||
// The chunk size for adding mods to the indexing database. If the request size
|
||||
// The chunk size for adding projects to the indexing database. If the request size
|
||||
// is too large (>10MiB) then the request fails with an error. This chunk size
|
||||
// assumes a max average size of 1KiB per mod to avoid this cap.
|
||||
// assumes a max average size of 1KiB per project to avoid this cap.
|
||||
const MEILISEARCH_CHUNK_SIZE: usize = 10000;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IndexingSettings {
|
||||
pub index_external: bool,
|
||||
pub index_local: bool,
|
||||
}
|
||||
|
||||
@@ -42,31 +41,24 @@ impl IndexingSettings {
|
||||
#[allow(dead_code)]
|
||||
pub fn from_env() -> Self {
|
||||
let index_local = true;
|
||||
let index_external = dotenv::var("INDEX_CURSEFORGE")
|
||||
.ok()
|
||||
.and_then(|b| b.parse::<bool>().ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
Self {
|
||||
index_external,
|
||||
index_local,
|
||||
}
|
||||
Self { index_local }
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn index_mods(
|
||||
pub async fn index_projects(
|
||||
pool: PgPool,
|
||||
settings: IndexingSettings,
|
||||
config: &SearchConfig,
|
||||
) -> Result<(), IndexingError> {
|
||||
let mut docs_to_add: Vec<UploadSearchMod> = vec![];
|
||||
let mut docs_to_add: Vec<UploadSearchProject> = vec![];
|
||||
|
||||
if settings.index_local {
|
||||
docs_to_add.append(&mut index_local(pool.clone()).await?);
|
||||
}
|
||||
// Write Indices
|
||||
|
||||
add_mods(docs_to_add, config).await?;
|
||||
add_projects(docs_to_add, config).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -74,12 +66,12 @@ pub async fn index_mods(
|
||||
pub async fn reset_indices(config: &SearchConfig) -> Result<(), IndexingError> {
|
||||
let client = Client::new(&*config.address, &*config.key);
|
||||
|
||||
client.delete_index("relevance_mods").await?;
|
||||
client.delete_index("downloads_mods").await?;
|
||||
client.delete_index("follows_mods").await?;
|
||||
client.delete_index("alphabetically_mods").await?;
|
||||
client.delete_index("updated_mods").await?;
|
||||
client.delete_index("newest_mods").await?;
|
||||
client.delete_index("relevance_projects").await?;
|
||||
client.delete_index("downloads_projects").await?;
|
||||
client.delete_index("follows_projects").await?;
|
||||
client.delete_index("updated_projects").await?;
|
||||
client.delete_index("newest_projects").await?;
|
||||
client.delete_index("alphabetically_projects").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -87,7 +79,7 @@ pub async fn reconfigure_indices(config: &SearchConfig) -> Result<(), IndexingEr
|
||||
let client = Client::new(&*config.address, &*config.key);
|
||||
|
||||
// Relevance Index
|
||||
update_index(&client, "relevance_mods", {
|
||||
update_index(&client, "relevance_projects", {
|
||||
let mut relevance_rules = default_rules();
|
||||
relevance_rules.push_back("desc(downloads)".to_string());
|
||||
relevance_rules.into()
|
||||
@@ -95,7 +87,7 @@ pub async fn reconfigure_indices(config: &SearchConfig) -> Result<(), IndexingEr
|
||||
.await?;
|
||||
|
||||
// Downloads Index
|
||||
update_index(&client, "downloads_mods", {
|
||||
update_index(&client, "downloads_projects", {
|
||||
let mut downloads_rules = default_rules();
|
||||
downloads_rules.push_front("desc(downloads)".to_string());
|
||||
downloads_rules.into()
|
||||
@@ -103,7 +95,7 @@ pub async fn reconfigure_indices(config: &SearchConfig) -> Result<(), IndexingEr
|
||||
.await?;
|
||||
|
||||
// Follows Index
|
||||
update_index(&client, "follows_mods", {
|
||||
update_index(&client, "follows_projects", {
|
||||
let mut follows_rules = default_rules();
|
||||
follows_rules.push_front("desc(follows)".to_string());
|
||||
follows_rules.into()
|
||||
@@ -111,15 +103,15 @@ pub async fn reconfigure_indices(config: &SearchConfig) -> Result<(), IndexingEr
|
||||
.await?;
|
||||
|
||||
// Alphabetically Index
|
||||
update_index(&client, "alphabetically_mods", {
|
||||
update_index(&client, "alphabetically_projects", {
|
||||
let mut alphabetically_rules = default_rules();
|
||||
alphabetically_rules.push_front("desc(title)".to_string());
|
||||
alphabetically_rules.into()
|
||||
})
|
||||
.await?;
|
||||
.await?;
|
||||
|
||||
// Updated Index
|
||||
update_index(&client, "updated_mods", {
|
||||
update_index(&client, "updated_projects", {
|
||||
let mut updated_rules = default_rules();
|
||||
updated_rules.push_front("desc(modified_timestamp)".to_string());
|
||||
updated_rules.into()
|
||||
@@ -127,7 +119,7 @@ pub async fn reconfigure_indices(config: &SearchConfig) -> Result<(), IndexingEr
|
||||
.await?;
|
||||
|
||||
// Created Index
|
||||
update_index(&client, "newest_mods", {
|
||||
update_index(&client, "newest_projects", {
|
||||
let mut newest_rules = default_rules();
|
||||
newest_rules.push_front("desc(created_timestamp)".to_string());
|
||||
newest_rules.into()
|
||||
@@ -147,7 +139,7 @@ async fn update_index<'a>(
|
||||
Err(meilisearch_sdk::errors::Error::MeiliSearchError {
|
||||
error_code: meilisearch_sdk::errors::ErrorCode::IndexNotFound,
|
||||
..
|
||||
}) => client.create_index(name, Some("mod_id")).await?,
|
||||
}) => client.create_index(name, Some("project_id")).await?,
|
||||
Err(e) => {
|
||||
return Err(IndexingError::IndexDBError(e));
|
||||
}
|
||||
@@ -171,7 +163,7 @@ async fn create_index<'a>(
|
||||
..
|
||||
}) => {
|
||||
// Only create index and set settings if the index doesn't already exist
|
||||
let index = client.create_index(name, Some("mod_id")).await?;
|
||||
let index = client.create_index(name, Some("project_id")).await?;
|
||||
|
||||
index
|
||||
.set_settings(&default_settings().with_ranking_rules(rules()))
|
||||
@@ -186,72 +178,72 @@ async fn create_index<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_to_index(index: Index<'_>, mods: &[UploadSearchMod]) -> Result<(), IndexingError> {
|
||||
async fn add_to_index(index: Index<'_>, mods: &[UploadSearchProject]) -> Result<(), IndexingError> {
|
||||
for chunk in mods.chunks(MEILISEARCH_CHUNK_SIZE) {
|
||||
index.add_documents(chunk, Some("mod_id")).await?;
|
||||
index.add_documents(chunk, Some("project_id")).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_mods(
|
||||
mods: Vec<UploadSearchMod>,
|
||||
pub async fn add_projects(
|
||||
projects: Vec<UploadSearchProject>,
|
||||
config: &SearchConfig,
|
||||
) -> Result<(), IndexingError> {
|
||||
let client = Client::new(&*config.address, &*config.key);
|
||||
|
||||
// Relevance Index
|
||||
let relevance_index = create_index(&client, "relevance_mods", || {
|
||||
let relevance_index = create_index(&client, "relevance_projects", || {
|
||||
let mut relevance_rules = default_rules();
|
||||
relevance_rules.push_back("desc(downloads)".to_string());
|
||||
relevance_rules.into()
|
||||
})
|
||||
.await?;
|
||||
add_to_index(relevance_index, &mods).await?;
|
||||
add_to_index(relevance_index, &projects).await?;
|
||||
|
||||
// Downloads Index
|
||||
let downloads_index = create_index(&client, "downloads_mods", || {
|
||||
let downloads_index = create_index(&client, "downloads_projects", || {
|
||||
let mut downloads_rules = default_rules();
|
||||
downloads_rules.push_front("desc(downloads)".to_string());
|
||||
downloads_rules.into()
|
||||
})
|
||||
.await?;
|
||||
add_to_index(downloads_index, &mods).await?;
|
||||
add_to_index(downloads_index, &projects).await?;
|
||||
|
||||
// Follows Index
|
||||
let follows_index = create_index(&client, "follows_mods", || {
|
||||
let follows_index = create_index(&client, "follows_projects", || {
|
||||
let mut follows_rules = default_rules();
|
||||
follows_rules.push_front("desc(follows)".to_string());
|
||||
follows_rules.into()
|
||||
})
|
||||
.await?;
|
||||
add_to_index(follows_index, &mods).await?;
|
||||
add_to_index(follows_index, &projects).await?;
|
||||
|
||||
// Alphabetically Index
|
||||
let alphabetically_index = create_index(&client, "alphabetically_mods", || {
|
||||
let alphabetically_index = create_index(&client, "alphabetically_projects", || {
|
||||
let mut alphabetically_rules = default_rules();
|
||||
alphabetically_rules.push_front("desc(title)".to_string());
|
||||
alphabetically_rules.into()
|
||||
})
|
||||
.await?;
|
||||
add_to_index(alphabetically_index, &mods).await?;
|
||||
.await?;
|
||||
add_to_index(alphabetically_index, &projects).await?;
|
||||
|
||||
// Updated Index
|
||||
let updated_index = create_index(&client, "updated_mods", || {
|
||||
let updated_index = create_index(&client, "updated_projects", || {
|
||||
let mut updated_rules = default_rules();
|
||||
updated_rules.push_front("desc(modified_timestamp)".to_string());
|
||||
updated_rules.into()
|
||||
})
|
||||
.await?;
|
||||
add_to_index(updated_index, &mods).await?;
|
||||
add_to_index(updated_index, &projects).await?;
|
||||
|
||||
// Created Index
|
||||
let newest_index = create_index(&client, "newest_mods", || {
|
||||
let newest_index = create_index(&client, "newest_projects", || {
|
||||
let mut newest_rules = default_rules();
|
||||
newest_rules.push_front("desc(created_timestamp)".to_string());
|
||||
newest_rules.into()
|
||||
})
|
||||
.await?;
|
||||
add_to_index(newest_index, &mods).await?;
|
||||
add_to_index(newest_index, &projects).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -271,7 +263,7 @@ fn default_rules() -> VecDeque<String> {
|
||||
|
||||
fn default_settings() -> Settings {
|
||||
let displayed_attributes = vec![
|
||||
"mod_id".to_string(),
|
||||
"project_id".to_string(),
|
||||
"slug".to_string(),
|
||||
"author".to_string(),
|
||||
"title".to_string(),
|
||||
@@ -280,16 +272,13 @@ fn default_settings() -> Settings {
|
||||
"versions".to_string(),
|
||||
"downloads".to_string(),
|
||||
"follows".to_string(),
|
||||
"page_url".to_string(),
|
||||
"icon_url".to_string(),
|
||||
"author_url".to_string(),
|
||||
"date_created".to_string(),
|
||||
"date_modified".to_string(),
|
||||
"latest_version".to_string(),
|
||||
"license".to_string(),
|
||||
"client_side".to_string(),
|
||||
"server_side".to_string(),
|
||||
"host".to_string(),
|
||||
];
|
||||
|
||||
let searchable_attributes = vec![
|
||||
@@ -325,7 +314,7 @@ fn default_settings() -> Settings {
|
||||
// This isn't currenly used, but I wrote it and it works, so I'm
|
||||
// keeping this mess in case someone needs it in the future.
|
||||
#[allow(dead_code)]
|
||||
pub fn sort_mods(a: &str, b: &str) -> std::cmp::Ordering {
|
||||
pub fn sort_projects(a: &str, b: &str) -> std::cmp::Ordering {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
let cmp = a.contains('.').cmp(&b.contains('.'));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{add_mods, IndexingError, UploadSearchMod};
|
||||
use super::{add_projects, IndexingError, UploadSearchProject};
|
||||
use crate::search::SearchConfig;
|
||||
use std::sync::Mutex;
|
||||
|
||||
@@ -7,7 +7,7 @@ pub struct CreationQueue {
|
||||
// and I don't think this can deadlock. This queue requires fast
|
||||
// writes and then a single potentially slower read/write that
|
||||
// empties the queue.
|
||||
queue: Mutex<Vec<UploadSearchMod>>,
|
||||
queue: Mutex<Vec<UploadSearchProject>>,
|
||||
}
|
||||
|
||||
impl CreationQueue {
|
||||
@@ -17,11 +17,11 @@ impl CreationQueue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&self, search_mod: UploadSearchMod) {
|
||||
pub fn add(&self, search_project: UploadSearchProject) {
|
||||
// Can only panic if mutex is poisoned
|
||||
self.queue.lock().unwrap().push(search_mod);
|
||||
self.queue.lock().unwrap().push(search_project);
|
||||
}
|
||||
pub fn take(&self) -> Vec<UploadSearchMod> {
|
||||
pub fn take(&self) -> Vec<UploadSearchProject> {
|
||||
std::mem::replace(&mut *self.queue.lock().unwrap(), Vec::with_capacity(10))
|
||||
}
|
||||
}
|
||||
@@ -31,5 +31,5 @@ pub async fn index_queue(
|
||||
config: &SearchConfig,
|
||||
) -> Result<(), IndexingError> {
|
||||
let queue = queue.take();
|
||||
add_mods(queue, config).await
|
||||
add_projects(queue, config).await
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::models::error::ApiError;
|
||||
use crate::models::mods::SearchRequest;
|
||||
use crate::models::projects::SearchRequest;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::HttpResponse;
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -57,11 +57,11 @@ pub struct SearchConfig {
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
/// A mod document used for uploading mods to meilisearch's indices.
|
||||
/// A project document used for uploading projects to meilisearch's indices.
|
||||
/// This contains some extra data that is not returned by search results.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct UploadSearchMod {
|
||||
pub mod_id: String,
|
||||
pub struct UploadSearchProject {
|
||||
pub project_id: String,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
pub title: String,
|
||||
@@ -70,17 +70,15 @@ pub struct UploadSearchMod {
|
||||
pub versions: Vec<String>,
|
||||
pub follows: i32,
|
||||
pub downloads: i32,
|
||||
pub page_url: String,
|
||||
pub icon_url: String,
|
||||
pub author_url: String,
|
||||
pub latest_version: Cow<'static, str>,
|
||||
pub license: String,
|
||||
pub client_side: String,
|
||||
pub server_side: String,
|
||||
|
||||
/// RFC 3339 formatted creation date of the mod
|
||||
/// RFC 3339 formatted creation date of the project
|
||||
pub date_created: DateTime<Utc>,
|
||||
/// Unix timestamp of the creation date of the mod
|
||||
/// Unix timestamp of the creation date of the project
|
||||
pub created_timestamp: i64,
|
||||
/// RFC 3339 formatted date/time of last major modification (update)
|
||||
pub date_modified: DateTime<Utc>,
|
||||
@@ -92,15 +90,15 @@ pub struct UploadSearchMod {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SearchResults {
|
||||
pub hits: Vec<ResultSearchMod>,
|
||||
pub hits: Vec<ResultSearchProject>,
|
||||
pub offset: usize,
|
||||
pub limit: usize,
|
||||
pub total_hits: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ResultSearchMod {
|
||||
pub mod_id: String,
|
||||
pub struct ResultSearchProject {
|
||||
pub project_id: String,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
pub title: String,
|
||||
@@ -110,39 +108,34 @@ pub struct ResultSearchMod {
|
||||
pub versions: Vec<String>,
|
||||
pub downloads: i32,
|
||||
pub follows: i32,
|
||||
pub page_url: String,
|
||||
pub icon_url: String,
|
||||
pub author_url: String,
|
||||
/// RFC 3339 formatted creation date of the mod
|
||||
/// RFC 3339 formatted creation date of the project
|
||||
pub date_created: String,
|
||||
/// RFC 3339 formatted modification date of the mod
|
||||
/// RFC 3339 formatted modification date of the project
|
||||
pub date_modified: String,
|
||||
pub latest_version: String,
|
||||
pub license: String,
|
||||
pub client_side: String,
|
||||
pub server_side: String,
|
||||
|
||||
/// The host of the mod: Either `modrinth` or `curseforge`
|
||||
pub host: String,
|
||||
}
|
||||
|
||||
impl Document for UploadSearchMod {
|
||||
impl Document for UploadSearchProject {
|
||||
type UIDType = String;
|
||||
|
||||
fn get_uid(&self) -> &Self::UIDType {
|
||||
&self.mod_id
|
||||
&self.project_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Document for ResultSearchMod {
|
||||
impl Document for ResultSearchProject {
|
||||
type UIDType = String;
|
||||
|
||||
fn get_uid(&self) -> &Self::UIDType {
|
||||
&self.mod_id
|
||||
&self.project_id
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn search_for_mod(
|
||||
pub async fn search_for_project(
|
||||
info: &SearchRequest,
|
||||
config: &SearchConfig,
|
||||
) -> Result<SearchResults, SearchError> {
|
||||
@@ -160,12 +153,12 @@ pub async fn search_for_mod(
|
||||
let limit = info.limit.as_deref().unwrap_or("10").parse()?;
|
||||
|
||||
let index = match index {
|
||||
"relevance" => "relevance_mods",
|
||||
"downloads" => "downloads_mods",
|
||||
"follows" => "follows_mods",
|
||||
"alphabetically" => "alphabetically_mods",
|
||||
"updated" => "updated_mods",
|
||||
"newest" => "newest_mods",
|
||||
"relevance" => "relevance_projects",
|
||||
"downloads" => "downloads_projects",
|
||||
"follows" => "follows_projects",
|
||||
"updated" => "updated_projects",
|
||||
"newest" => "newest_projects",
|
||||
"alphabetically" => "alphabetically_projects",
|
||||
i => return Err(SearchError::InvalidIndex(i.to_string())),
|
||||
};
|
||||
|
||||
@@ -203,7 +196,7 @@ pub async fn search_for_mod(
|
||||
query.with_facet_filters(&why_must_you_do_this);
|
||||
}
|
||||
|
||||
let results = query.execute::<ResultSearchMod>().await?;
|
||||
let results = query.execute::<ResultSearchProject>().await?;
|
||||
|
||||
Ok(SearchResults {
|
||||
hits: results.hits.into_iter().map(|r| r.result).collect(),
|
||||
|
||||
Reference in New Issue
Block a user