use actix_web as aweb; use actix_web::{delete, get, post, web, HttpResponse}; use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES}; use std::collections::{BTreeMap, BTreeSet, HashSet}; use crate::error::ResponseError; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; #[post("/indexes/{index_uid}/settings")] pub async fn update_all( data: web::Data, path: web::Path, body: web::Json, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = body .into_inner() .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[get("/indexes/{index_uid}/settings")] pub async fn get_all( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data .db .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let stop_words_fst = index .main .stop_words_fst(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let stop_words = stop_words_fst .unwrap_or_default() .stream() .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; let stop_words: BTreeSet = stop_words.into_iter().collect(); let synonyms_fst = index .main .synonyms_fst(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or_default(); let synonyms_list = synonyms_fst .stream() .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut synonyms = BTreeMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { let alternative_list = index_synonyms .synonyms(&reader, synonym.as_bytes()) .map_err(|err| ResponseError::Internal(err.to_string()))?; if let Some(list) = alternative_list { let list = list .stream() .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; synonyms.insert(synonym, list); } } let ranking_rules = index .main .ranking_rules(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) .into_iter() .map(|r| r.to_string()) .collect(); let distinct_attribute = index .main .distinct_attribute(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let schema = index .main .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let searchable_attributes = schema.clone().map(|s| { s.indexed_name() .iter() .map(|s| (*s).to_string()) .collect::>() }); let displayed_attributes = schema.clone().map(|s| { s.displayed_name() .iter() .map(|s| (*s).to_string()) .collect::>() }); let accept_new_fields = schema.map(|s| s.accept_new_fields()); let settings = Settings { ranking_rules: Some(Some(ranking_rules)), distinct_attribute: Some(distinct_attribute), searchable_attributes: Some(searchable_attributes), displayed_attributes: Some(displayed_attributes), stop_words: Some(Some(stop_words)), synonyms: Some(Some(synonyms)), accept_new_fields: Some(accept_new_fields), }; Ok(HttpResponse::Ok().json(settings)) } #[delete("/indexes/{index_uid}/settings")] pub async fn delete_all( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = SettingsUpdate { ranking_rules: UpdateState::Clear, distinct_attribute: UpdateState::Clear, primary_key: UpdateState::Clear, searchable_attributes: UpdateState::Clear, displayed_attributes: UpdateState::Clear, stop_words: UpdateState::Clear, synonyms: UpdateState::Clear, accept_new_fields: UpdateState::Clear, }; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[get("/indexes/{index_uid}/settings/ranking-rules")] pub async fn get_rules( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data .db .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let ranking_rules = index .main .ranking_rules(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) .into_iter() .map(|r| r.to_string()) .collect::>(); Ok(HttpResponse::Ok().json(ranking_rules)) } #[post("/indexes/{index_uid}/settings/ranking-rules")] pub async fn update_rules( data: web::Data, path: web::Path, body: web::Json>>, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { ranking_rules: Some(body.into_inner()), ..Settings::default() }; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = settings .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[delete("/indexes/{index_uid}/settings/ranking-rules")] pub async fn delete_rules( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = SettingsUpdate { ranking_rules: UpdateState::Clear, ..SettingsUpdate::default() }; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[get("/indexes/{index_uid}/settings/distinct-attribute")] pub async fn get_distinct( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data .db .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let distinct_attribute = index .main .distinct_attribute(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Ok().json(distinct_attribute)) } #[post("/indexes/{index_uid}/settings/distinct-attribute")] pub async fn update_distinct( data: web::Data, path: web::Path, body: web::Json>, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { distinct_attribute: Some(body.into_inner()), ..Settings::default() }; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = settings .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[delete("/indexes/{index_uid}/settings/distinct-attribute")] pub async fn delete_distinct( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = SettingsUpdate { distinct_attribute: UpdateState::Clear, ..SettingsUpdate::default() }; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[get("/indexes/{index_uid}/settings/searchable-attributes")] pub async fn get_searchable( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data .db .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let schema = index .main .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let searchable_attributes: Option> = schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect()); Ok(HttpResponse::Ok().json(searchable_attributes)) } #[post("/indexes/{index_uid}/settings/searchable-attributes")] pub async fn update_searchable( data: web::Data, path: web::Path, body: web::Json>>, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { searchable_attributes: Some(body.into_inner()), ..Settings::default() }; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = settings .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[delete("/indexes/{index_uid}/settings/searchable-attributes")] pub async fn delete_searchable( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { searchable_attributes: UpdateState::Clear, ..SettingsUpdate::default() }; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[get("/indexes/{index_uid}/settings/displayed-attributes")] pub async fn get_displayed( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data .db .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let schema = index .main .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let displayed_attributes: Option> = schema.map(|s| { s.displayed_name() .iter() .map(|i| (*i).to_string()) .collect() }); Ok(HttpResponse::Ok().json(displayed_attributes)) } #[post("/indexes/{index_uid}/settings/displayed-attributes")] pub async fn update_displayed( data: web::Data, path: web::Path, body: web::Json>>, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { displayed_attributes: Some(body.into_inner()), ..Settings::default() }; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = settings .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[delete("/indexes/{index_uid}/settings/displayed-attributes")] pub async fn delete_displayed( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { displayed_attributes: UpdateState::Clear, ..SettingsUpdate::default() }; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } #[get("/indexes/{index_uid}/settings/accept-new-fields")] pub async fn get_accept_new_fields( data: web::Data, path: web::Path, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data .db .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let schema = index .main .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let accept_new_fields = schema.map(|s| s.accept_new_fields()); Ok(HttpResponse::Ok().json(accept_new_fields)) } #[post("/indexes/{index_uid}/settings/accept-new-fields")] pub async fn update_accept_new_fields( data: web::Data, path: web::Path, body: web::Json>, ) -> aweb::Result { let index = data .db .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { accept_new_fields: Some(body.into_inner()), ..Settings::default() }; let mut writer = data .db .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = settings .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; let update_id = index .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; writer .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) }