2020-06-30 19:23:52 +02:00
|
|
|
/// This module is used for the indexing from any source.
|
|
|
|
|
pub mod local_import;
|
|
|
|
|
|
2021-05-30 15:02:07 -07:00
|
|
|
use crate::search::{SearchConfig, UploadSearchProject};
|
2020-07-27 18:54:10 -05:00
|
|
|
use local_import::index_local;
|
2020-06-30 19:23:52 +02:00
|
|
|
use meilisearch_sdk::client::Client;
|
2020-07-27 18:54:10 -05:00
|
|
|
use meilisearch_sdk::indexes::Index;
|
2023-03-04 10:53:26 -05:00
|
|
|
use meilisearch_sdk::settings::{PaginationSetting, Settings};
|
2020-07-23 22:46:33 +02:00
|
|
|
use sqlx::postgres::PgPool;
|
2020-07-03 12:44:39 -05:00
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
|
|
#[derive(Error, Debug)]
|
|
|
|
|
pub enum IndexingError {
|
|
|
|
|
#[error("Error while connecting to the MeiliSearch database")]
|
2022-03-27 19:12:42 -07:00
|
|
|
Indexing(#[from] meilisearch_sdk::errors::Error),
|
2020-07-03 12:44:39 -05:00
|
|
|
#[error("Error while serializing or deserializing JSON: {0}")]
|
2022-03-27 19:12:42 -07:00
|
|
|
Serde(#[from] serde_json::Error),
|
2020-07-03 12:44:39 -05:00
|
|
|
#[error("Database Error: {0}")]
|
2022-03-27 19:12:42 -07:00
|
|
|
Sqlx(#[from] sqlx::error::Error),
|
2021-01-18 10:10:45 -07:00
|
|
|
#[error("Database Error: {0}")]
|
2022-03-27 19:12:42 -07:00
|
|
|
Database(#[from] crate::database::models::DatabaseError),
|
2020-07-03 12:44:39 -05:00
|
|
|
#[error("Environment Error")]
|
2022-10-22 21:23:31 -07:00
|
|
|
Env(#[from] dotenvy::Error),
|
2022-03-27 19:12:42 -07:00
|
|
|
#[error("Error while awaiting index creation task")]
|
|
|
|
|
Task,
|
2020-07-03 12:44:39 -05:00
|
|
|
}
|
2020-06-30 19:23:52 +02:00
|
|
|
|
2021-05-30 15:02:07 -07:00
|
|
|
// The chunk size for adding projects to the indexing database. If the request size
|
2020-07-15 17:01:49 -05:00
|
|
|
// is too large (>10MiB) then the request fails with an error. This chunk size
|
2021-05-30 15:02:07 -07:00
|
|
|
// assumes a max average size of 1KiB per project to avoid this cap.
|
2020-07-15 17:01:49 -05:00
|
|
|
const MEILISEARCH_CHUNK_SIZE: usize = 10000;
|
|
|
|
|
|
2023-08-02 14:43:04 -07:00
|
|
|
pub async fn index_projects(pool: PgPool, config: &SearchConfig) -> Result<(), IndexingError> {
|
2021-05-30 15:02:07 -07:00
|
|
|
let mut docs_to_add: Vec<UploadSearchProject> = vec![];
|
2023-11-11 16:40:10 -08:00
|
|
|
let mut additional_fields: Vec<String> = vec![];
|
2020-07-15 17:01:49 -05:00
|
|
|
|
2023-11-11 16:40:10 -08:00
|
|
|
let (mut uploads, mut loader_fields) = index_local(pool.clone()).await?;
|
|
|
|
|
docs_to_add.append(&mut uploads);
|
|
|
|
|
additional_fields.append(&mut loader_fields);
|
2020-06-30 19:23:52 +02:00
|
|
|
|
2022-03-27 19:12:42 -07:00
|
|
|
// Write Indices
|
2023-11-11 16:40:10 -08:00
|
|
|
add_projects(docs_to_add, additional_fields, config).await?;
|
2020-06-30 19:23:52 +02:00
|
|
|
|
2020-07-27 18:54:10 -05:00
|
|
|
Ok(())
|
|
|
|
|
}
|
2020-07-15 17:01:49 -05:00
|
|
|
|
2022-03-27 19:12:42 -07:00
|
|
|
async fn create_index(
|
|
|
|
|
client: &Client,
|
|
|
|
|
name: &'static str,
|
|
|
|
|
custom_rules: Option<&'static [&'static str]>,
|
|
|
|
|
) -> Result<Index, IndexingError> {
|
|
|
|
|
client
|
|
|
|
|
.delete_index(name)
|
|
|
|
|
.await?
|
|
|
|
|
.wait_for_completion(client, None, None)
|
2020-07-31 20:18:23 -05:00
|
|
|
.await?;
|
|
|
|
|
|
2020-07-27 18:54:10 -05:00
|
|
|
match client.get_index(name).await {
|
2022-03-27 19:12:42 -07:00
|
|
|
Ok(index) => {
|
|
|
|
|
index
|
|
|
|
|
.set_settings(&default_settings())
|
|
|
|
|
.await?
|
|
|
|
|
.wait_for_completion(client, None, None)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
Ok(index)
|
|
|
|
|
}
|
|
|
|
|
Err(meilisearch_sdk::errors::Error::Meilisearch(
|
|
|
|
|
meilisearch_sdk::errors::MeilisearchError {
|
|
|
|
|
error_code: meilisearch_sdk::errors::ErrorCode::IndexNotFound,
|
|
|
|
|
..
|
|
|
|
|
},
|
|
|
|
|
)) => {
|
2020-07-27 18:54:10 -05:00
|
|
|
// Only create index and set settings if the index doesn't already exist
|
2023-11-11 16:40:10 -08:00
|
|
|
let task = client.create_index(name, Some("version_id")).await?;
|
2022-03-27 19:12:42 -07:00
|
|
|
let task = task.wait_for_completion(client, None, None).await?;
|
|
|
|
|
let index = task
|
|
|
|
|
.try_make_index(client)
|
|
|
|
|
.map_err(|_| IndexingError::Task)?;
|
|
|
|
|
|
|
|
|
|
let mut settings = default_settings();
|
|
|
|
|
|
|
|
|
|
if let Some(custom_rules) = custom_rules {
|
|
|
|
|
settings = settings.with_ranking_rules(custom_rules);
|
|
|
|
|
}
|
2020-07-27 18:54:10 -05:00
|
|
|
|
|
|
|
|
index
|
2022-03-27 19:12:42 -07:00
|
|
|
.set_settings(&settings)
|
|
|
|
|
.await?
|
|
|
|
|
.wait_for_completion(client, None, None)
|
2020-07-27 18:54:10 -05:00
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
Ok(index)
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
log::warn!("Unhandled error while creating index: {}", e);
|
2022-03-27 19:12:42 -07:00
|
|
|
Err(IndexingError::Indexing(e))
|
2020-07-27 18:54:10 -05:00
|
|
|
}
|
2020-07-15 17:01:49 -05:00
|
|
|
}
|
2020-07-27 18:54:10 -05:00
|
|
|
}
|
2020-06-30 19:23:52 +02:00
|
|
|
|
2022-02-27 21:44:00 -07:00
|
|
|
async fn add_to_index(
|
2022-03-27 19:12:42 -07:00
|
|
|
client: &Client,
|
|
|
|
|
index: Index,
|
2022-02-27 21:44:00 -07:00
|
|
|
mods: &[UploadSearchProject],
|
|
|
|
|
) -> Result<(), IndexingError> {
|
2020-07-27 18:54:10 -05:00
|
|
|
for chunk in mods.chunks(MEILISEARCH_CHUNK_SIZE) {
|
2022-03-27 19:12:42 -07:00
|
|
|
index
|
2023-11-11 16:40:10 -08:00
|
|
|
.add_documents(chunk, Some("version_id"))
|
2022-03-27 19:12:42 -07:00
|
|
|
.await?
|
|
|
|
|
.wait_for_completion(client, None, None)
|
|
|
|
|
.await?;
|
2020-07-27 18:54:10 -05:00
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2020-06-30 19:23:52 +02:00
|
|
|
|
2022-03-27 19:12:42 -07:00
|
|
|
async fn create_and_add_to_index(
|
|
|
|
|
client: &Client,
|
|
|
|
|
projects: &[UploadSearchProject],
|
2023-11-11 16:40:10 -08:00
|
|
|
additional_fields: &[String],
|
2021-10-12 11:26:59 +08:00
|
|
|
name: &'static str,
|
2022-03-27 19:12:42 -07:00
|
|
|
custom_rules: Option<&'static [&'static str]>,
|
2020-10-19 14:23:05 -07:00
|
|
|
) -> Result<(), IndexingError> {
|
2022-03-27 19:12:42 -07:00
|
|
|
let index = create_index(client, name, custom_rules).await?;
|
2023-11-11 16:40:10 -08:00
|
|
|
|
|
|
|
|
let mut new_filterable_attributes = index.get_filterable_attributes().await?;
|
2023-11-26 19:29:59 -08:00
|
|
|
let mut new_displayed_attributes = index.get_displayed_attributes().await?;
|
|
|
|
|
|
2023-11-11 16:40:10 -08:00
|
|
|
new_filterable_attributes.extend(additional_fields.iter().map(|s| s.to_string()));
|
2023-11-26 19:29:59 -08:00
|
|
|
new_displayed_attributes.extend(additional_fields.iter().map(|s| s.to_string()));
|
2023-11-11 16:40:10 -08:00
|
|
|
index
|
|
|
|
|
.set_filterable_attributes(new_filterable_attributes)
|
|
|
|
|
.await?;
|
2023-11-26 19:29:59 -08:00
|
|
|
index
|
|
|
|
|
.set_displayed_attributes(new_displayed_attributes)
|
|
|
|
|
.await?;
|
2023-11-11 16:40:10 -08:00
|
|
|
|
2022-03-27 19:12:42 -07:00
|
|
|
add_to_index(client, index, projects).await?;
|
2021-10-12 11:26:59 +08:00
|
|
|
Ok(())
|
|
|
|
|
}
|
2021-03-07 18:44:24 -07:00
|
|
|
|
2021-10-12 11:26:59 +08:00
|
|
|
pub async fn add_projects(
|
|
|
|
|
projects: Vec<UploadSearchProject>,
|
2023-11-11 16:40:10 -08:00
|
|
|
additional_fields: Vec<String>,
|
2021-10-12 11:26:59 +08:00
|
|
|
config: &SearchConfig,
|
|
|
|
|
) -> Result<(), IndexingError> {
|
|
|
|
|
let client = config.make_client();
|
|
|
|
|
|
2023-11-11 16:40:10 -08:00
|
|
|
create_and_add_to_index(&client, &projects, &additional_fields, "projects", None).await?;
|
2022-03-27 19:12:42 -07:00
|
|
|
|
2022-02-27 21:44:00 -07:00
|
|
|
create_and_add_to_index(
|
|
|
|
|
&client,
|
|
|
|
|
&projects,
|
2023-11-11 16:40:10 -08:00
|
|
|
&additional_fields,
|
2022-03-27 19:12:42 -07:00
|
|
|
"projects_filtered",
|
|
|
|
|
Some(&[
|
|
|
|
|
"sort",
|
|
|
|
|
"words",
|
|
|
|
|
"typo",
|
|
|
|
|
"proximity",
|
|
|
|
|
"attribute",
|
|
|
|
|
"exactness",
|
|
|
|
|
]),
|
2021-10-12 11:26:59 +08:00
|
|
|
)
|
2020-07-27 18:54:10 -05:00
|
|
|
.await?;
|
2020-06-30 19:23:52 +02:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn default_settings() -> Settings {
|
|
|
|
|
Settings::new()
|
2023-11-11 16:40:10 -08:00
|
|
|
.with_distinct_attribute("project_id")
|
2021-10-12 11:26:59 +08:00
|
|
|
.with_displayed_attributes(DEFAULT_DISPLAYED_ATTRIBUTES)
|
|
|
|
|
.with_searchable_attributes(DEFAULT_SEARCHABLE_ATTRIBUTES)
|
2022-03-27 19:12:42 -07:00
|
|
|
.with_sortable_attributes(DEFAULT_SORTABLE_ATTRIBUTES)
|
|
|
|
|
.with_filterable_attributes(DEFAULT_ATTRIBUTES_FOR_FACETING)
|
2023-03-04 10:53:26 -05:00
|
|
|
.with_pagination(PaginationSetting {
|
|
|
|
|
max_total_hits: 2147483647,
|
|
|
|
|
})
|
2020-06-30 19:23:52 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-12 11:26:59 +08:00
|
|
|
const DEFAULT_DISPLAYED_ATTRIBUTES: &[&str] = &[
|
|
|
|
|
"project_id",
|
2023-11-11 16:40:10 -08:00
|
|
|
"version_id",
|
2023-11-16 10:36:03 -08:00
|
|
|
"project_types",
|
2021-10-12 11:26:59 +08:00
|
|
|
"slug",
|
|
|
|
|
"author",
|
2023-12-01 19:15:00 -08:00
|
|
|
"name",
|
2021-10-12 11:26:59 +08:00
|
|
|
"description",
|
|
|
|
|
"categories",
|
2022-07-31 15:26:25 -07:00
|
|
|
"display_categories",
|
2021-10-12 11:26:59 +08:00
|
|
|
"downloads",
|
|
|
|
|
"follows",
|
|
|
|
|
"icon_url",
|
|
|
|
|
"date_created",
|
|
|
|
|
"date_modified",
|
|
|
|
|
"latest_version",
|
|
|
|
|
"license",
|
2022-08-03 02:31:56 -04:00
|
|
|
"gallery",
|
2022-12-30 13:56:41 -07:00
|
|
|
"featured_gallery",
|
|
|
|
|
"color",
|
2021-10-12 11:26:59 +08:00
|
|
|
];
|
|
|
|
|
|
2023-12-01 19:15:00 -08:00
|
|
|
const DEFAULT_SEARCHABLE_ATTRIBUTES: &[&str] = &["name", "description", "author", "slug"];
|
2021-10-12 11:26:59 +08:00
|
|
|
|
|
|
|
|
const DEFAULT_ATTRIBUTES_FOR_FACETING: &[&str] = &[
|
|
|
|
|
"categories",
|
|
|
|
|
"license",
|
2023-11-16 10:36:03 -08:00
|
|
|
"project_types",
|
2022-03-27 19:12:42 -07:00
|
|
|
"downloads",
|
|
|
|
|
"follows",
|
|
|
|
|
"author",
|
2023-12-01 19:15:00 -08:00
|
|
|
"name",
|
2022-03-27 19:12:42 -07:00
|
|
|
"date_created",
|
2023-09-15 21:29:16 +02:00
|
|
|
"created_timestamp",
|
2022-03-27 19:12:42 -07:00
|
|
|
"date_modified",
|
2023-09-15 21:29:16 +02:00
|
|
|
"modified_timestamp",
|
2022-09-06 00:39:50 -04:00
|
|
|
"project_id",
|
2022-11-29 23:53:24 -05:00
|
|
|
"open_source",
|
2022-12-30 13:56:41 -07:00
|
|
|
"color",
|
2021-10-12 11:26:59 +08:00
|
|
|
];
|
2020-11-03 18:55:50 -06:00
|
|
|
|
2022-03-27 19:12:42 -07:00
|
|
|
const DEFAULT_SORTABLE_ATTRIBUTES: &[&str] =
|
|
|
|
|
&["downloads", "follows", "date_created", "date_modified"];
|