diff --git a/src/routes/settings/attributes_for_faceting.rs b/src/routes/settings/attributes_for_faceting.rs new file mode 100644 index 000000000..6c881cff3 --- /dev/null +++ b/src/routes/settings/attributes_for_faceting.rs @@ -0,0 +1,43 @@ +use actix_web::{web, HttpResponse, get}; + +use crate::error::{Error, ResponseError}; +use crate::helpers::Authentication; +use crate::make_update_delete_routes; +use crate::Data; + +#[get( + "/indexes/{index_uid}/settings/attributes-for-faceting", + wrap = "Authentication::Private" +)] +async fn get( + data: web::Data, + index_uid: web::Path, +) -> Result { + let index = data + .db + .load() + .open_index(&index_uid.as_ref()) + .ok_or(Error::index_not_found(&index_uid.as_ref()))?; + + let attributes_for_faceting = data.db.load().main_read::<_, _, ResponseError>(|reader| { + let schema = index.main.schema(reader)?; + let attrs = index.main.attributes_for_faceting(reader)?; + let attr_names = match (&schema, &attrs) { + (Some(schema), Some(attrs)) => attrs + .iter() + .filter_map(|&id| schema.name(id)) + .map(str::to_string) + .collect(), + _ => vec![], + }; + Ok(attr_names) + })?; + + Ok(HttpResponse::Ok().json(attributes_for_faceting)) +} + +make_update_delete_routes!( + "/indexes/{index_uid}/settings/attributes-for-faceting", + Vec, + attributes_for_faceting +); diff --git a/src/routes/settings/displayed_attributes.rs b/src/routes/settings/displayed_attributes.rs new file mode 100644 index 000000000..b9f36f718 --- /dev/null +++ b/src/routes/settings/displayed_attributes.rs @@ -0,0 +1,25 @@ +use std::collections::HashSet; + +use actix_web::{web, HttpResponse, get}; + +use crate::error::{Error, ResponseError}; +use crate::helpers::Authentication; +use crate::make_update_delete_routes; +use crate::Data; + +#[get( + "/indexes/{index_uid}/settings/displayed-attributes", + wrap = "Authentication::Private" +)] +async fn get( + data: web::Data, + index_uid: web::Path, +) -> Result { + todo!() +} + +make_update_delete_routes!( + "/indexes/{index_uid}/settings/displayed-attributes", + HashSet, + displayed_attributes +); diff --git a/src/routes/settings/distinct_attributes.rs b/src/routes/settings/distinct_attributes.rs new file mode 100644 index 000000000..7b991f861 --- /dev/null +++ b/src/routes/settings/distinct_attributes.rs @@ -0,0 +1,36 @@ +use crate::make_update_delete_routes; +use actix_web::{web, HttpResponse, get}; + +use crate::error::{Error, ResponseError}; +use crate::helpers::Authentication; +use crate::Data; + +#[get( + "/indexes/{index_uid}/settings/distinct-attribute", + wrap = "Authentication::Private" +)] +async fn get( + data: web::Data, + index_uid: web::Path, +) -> Result { + let index = data + .db + .load() + .open_index(&index_uid.as_ref()) + .ok_or(Error::index_not_found(&index_uid.as_ref()))?; + let reader = data.db.load().main_read_txn()?; + let distinct_attribute_id = index.main.distinct_attribute(&reader)?; + let schema = index.main.schema(&reader)?; + let distinct_attribute = match (schema, distinct_attribute_id) { + (Some(schema), Some(id)) => schema.name(id).map(str::to_string), + _ => None, + }; + + Ok(HttpResponse::Ok().json(distinct_attribute)) +} + +make_update_delete_routes!( + "/indexes/{index_uid}/settings/distinct-attribute", + String, + distinct_attribute +); diff --git a/src/routes/settings/mod.rs b/src/routes/settings/mod.rs new file mode 100644 index 000000000..d54ad469c --- /dev/null +++ b/src/routes/settings/mod.rs @@ -0,0 +1,190 @@ +use actix_web::{web, HttpResponse, delete, get, post}; +use log::error; + +use crate::Data; +use crate::error::ResponseError; +use crate::updates::Settings; +use crate::helpers::Authentication; + +#[macro_export] +macro_rules! make_setting_route { + ($route:literal, $type:ty, $attr:ident) => { + mod $attr { + use actix_web::{web, HttpResponse}; + + use crate::data; + use crate::error::ResponseError; + use crate::helpers::Authentication; + use crate::updates::Settings; + + #[actix_web::delete($route, wrap = "Authentication::Private")] + pub async fn delete( + data: web::Data, + index_uid: web::Path, + ) -> Result { + use crate::updates::Settings; + let settings = Settings { + $attr: Some(None), + ..Default::default() + }; + match data.update_settings(index_uid.as_ref(), settings).await { + Ok(update_status) => { + let json = serde_json::to_string(&update_status).unwrap(); + Ok(HttpResponse::Ok().body(json)) + } + Err(e) => { + log::error!("{}", e); + unimplemented!(); + } + } + } + + #[actix_web::post($route, wrap = "Authentication::Private")] + pub async fn update( + data: actix_web::web::Data, + index_uid: actix_web::web::Path, + body: actix_web::web::Json>, + ) -> std::result::Result { + let settings = Settings { + $attr: Some(body.into_inner()), + ..Default::default() + }; + + match data.update_settings(index_uid.as_ref(), settings).await { + Ok(update_status) => { + let json = serde_json::to_string(&update_status).unwrap(); + Ok(HttpResponse::Ok().body(json)) + } + Err(e) => { + log::error!("{}", e); + unimplemented!(); + } + } + } + + #[actix_web::get($route, wrap = "Authentication::Private")] + pub async fn get( + data: actix_web::web::Data, + index_uid: actix_web::web::Path, + ) -> std::result::Result { + match data.settings(index_uid.as_ref()) { + Ok(settings) => { + let setting = settings.$attr; + let json = serde_json::to_string(&setting).unwrap(); + Ok(HttpResponse::Ok().body(json)) + } + Err(e) => { + log::error!("{}", e); + unimplemented!(); + } + } + } + } + }; +} + +make_setting_route!( + "/indexes/{index_uid}/settings/attributes-for-faceting", + std::collections::HashMap, + faceted_attributes +); + +make_setting_route!( + "/indexes/{index_uid}/settings/displayed-attributes", + Vec, + displayed_attributes +); + +make_setting_route!( + "/indexes/{index_uid}/settings/searchable-attributes", + Vec, + searchable_attributes +); + +//make_setting_route!( + //"/indexes/{index_uid}/settings/distinct-attribute", + //String, + //distinct_attribute +//); + +//make_setting_route!( + //"/indexes/{index_uid}/settings/ranking-rules", + //Vec, + //ranking_rules +//); + +macro_rules! create_services { + ($($mod:ident),*) => { + pub fn services(cfg: &mut web::ServiceConfig) { + cfg + .service(update_all) + .service(get_all) + .service(delete_all) + $( + .service($mod::get) + .service($mod::update) + .service($mod::delete) + )*; + } + }; +} + +create_services!( + faceted_attributes, + displayed_attributes, + searchable_attributes +); + +#[post("/indexes/{index_uid}/settings", wrap = "Authentication::Private")] +async fn update_all( + data: web::Data, + index_uid: web::Path, + body: web::Json, +) -> Result { + match data.update_settings(index_uid.as_ref(), body.into_inner()).await { + Ok(update_result) => { + let json = serde_json::to_string(&update_result).unwrap(); + Ok(HttpResponse::Ok().body(json)) + } + Err(e) => { + error!("{}", e); + unimplemented!(); + } + } +} + +#[get("/indexes/{index_uid}/settings", wrap = "Authentication::Private")] +async fn get_all( + data: web::Data, + index_uid: web::Path, +) -> Result { + match data.settings(index_uid.as_ref()) { + Ok(settings) => { + let json = serde_json::to_string(&settings).unwrap(); + Ok(HttpResponse::Ok().body(json)) + } + Err(e) => { + error!("{}", e); + unimplemented!(); + } + } +} + +#[delete("/indexes/{index_uid}/settings", wrap = "Authentication::Private")] +async fn delete_all( + data: web::Data, + index_uid: web::Path, +) -> Result { + let settings = Settings::cleared(); + match data.update_settings(index_uid.as_ref(), settings).await { + Ok(update_result) => { + let json = serde_json::to_string(&update_result).unwrap(); + Ok(HttpResponse::Ok().body(json)) + } + Err(e) => { + error!("{}", e); + unimplemented!(); + } + } +} + diff --git a/src/routes/settings/ranking_rules.rs b/src/routes/settings/ranking_rules.rs new file mode 100644 index 000000000..e0872954a --- /dev/null +++ b/src/routes/settings/ranking_rules.rs @@ -0,0 +1,23 @@ +use crate::make_update_delete_routes; +use actix_web::{web, HttpResponse, get}; + +use crate::error::{Error, ResponseError}; +use crate::helpers::Authentication; +use crate::Data; + +#[get( + "/indexes/{index_uid}/settings/ranking-rules", + wrap = "Authentication::Private" +)] +async fn get( + data: web::Data, + index_uid: web::Path, +) -> Result { + todo!() +} + +make_update_delete_routes!( + "/indexes/{index_uid}/settings/ranking-rules", + Vec, + ranking_rules +); diff --git a/src/routes/settings/searchable_attributes.rs b/src/routes/settings/searchable_attributes.rs new file mode 100644 index 000000000..a337b0435 --- /dev/null +++ b/src/routes/settings/searchable_attributes.rs @@ -0,0 +1,34 @@ +use actix_web::{web, HttpResponse, get}; + +use crate::data::get_indexed_attributes; +use crate::error::{Error, ResponseError}; +use crate::helpers::Authentication; +use crate::make_update_delete_routes; +use crate::Data; + +#[get( + "/indexes/{index_uid}/settings/searchable-attributes", + wrap = "Authentication::Private" +)] +async fn get( + data: web::Data, + index_uid: web::Path, +) -> Result { + let index = data + .db + .load() + .open_index(&index_uid.as_ref()) + + .ok_or(Error::index_not_found(&index_uid.as_ref()))?; + let reader = data.db.load().main_read_txn()?; + let schema = index.main.schema(&reader)?; + let searchable_attributes: Option> = schema.as_ref().map(get_indexed_attributes); + + Ok(HttpResponse::Ok().json(searchable_attributes)) +} + +make_update_delete_routes!( + "/indexes/{index_uid}/settings/searchable-attributes", + Vec, + searchable_attributes +); diff --git a/src/routes/settings/stop_words.rs b/src/routes/settings/stop_words.rs new file mode 100644 index 000000000..05a753f46 --- /dev/null +++ b/src/routes/settings/stop_words.rs @@ -0,0 +1,33 @@ +use std::collections::BTreeSet; + +use crate::make_update_delete_routes; +use actix_web::{web, HttpResponse, get}; + +use crate::error::{Error, ResponseError}; +use crate::helpers::Authentication; +use crate::Data; + +#[get( + "/indexes/{index_uid}/settings/stop-words", + wrap = "Authentication::Private" +)] +async fn get( + data: web::Data, + index_uid: web::Path, +) -> Result { + let index = data + .db + .load() + .open_index(&index_uid.as_ref()) + .ok_or(Error::index_not_found(&index_uid.as_ref()))?; + let reader = data.db.load().main_read_txn()?; + let stop_words = index.main.stop_words(&reader)?; + + Ok(HttpResponse::Ok().json(stop_words)) +} + +make_update_delete_routes!( + "/indexes/{index_uid}/settings/stop-words", + BTreeSet, + stop_words +); diff --git a/src/routes/settings/synonyms.rs b/src/routes/settings/synonyms.rs new file mode 100644 index 000000000..e5b5b2afd --- /dev/null +++ b/src/routes/settings/synonyms.rs @@ -0,0 +1,43 @@ +use std::collections::BTreeMap; + +use actix_web::{web, HttpResponse, get}; +use indexmap::IndexMap; + +use crate::error::{Error, ResponseError}; +use crate::helpers::Authentication; +use crate::make_update_delete_routes; +use crate::Data; + +#[get( + "/indexes/{index_uid}/settings/synonyms", + wrap = "Authentication::Private" +)] +async fn get( + data: web::Data, + index_uid: web::Path, +) -> Result { + let index = data + .db + .load() + .open_index(&index_uid.as_ref()) + .ok_or(Error::index_not_found(&index_uid.as_ref()))?; + + let reader = data.db.load().main_read_txn()?; + + let synonyms_list = index.main.synonyms(&reader)?; + + let mut synonyms = IndexMap::new(); + let index_synonyms = &index.synonyms; + for synonym in synonyms_list { + let list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; + synonyms.insert(synonym, list); + } + + Ok(HttpResponse::Ok().json(synonyms)) +} + +make_update_delete_routes!( + "/indexes/{index_uid}/settings/synonyms", + BTreeMap>, + synonyms +);