diff --git a/meilisearch-http/src/data/mod.rs b/meilisearch-http/src/data/mod.rs index ec64192f7..c7979210e 100644 --- a/meilisearch-http/src/data/mod.rs +++ b/meilisearch-http/src/data/mod.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use sha2::Digest; -use crate::index::Settings; +use crate::index::{Checked, Settings}; use crate::index_controller::{IndexController, IndexStats, Stats}; use crate::index_controller::{IndexMetadata, IndexSettings}; use crate::option::Opt; @@ -74,7 +74,7 @@ impl Data { Ok(Data { inner }) } - pub async fn settings(&self, uid: String) -> anyhow::Result { + pub async fn settings(&self, uid: String) -> anyhow::Result> { self.index_controller.settings(uid).await } diff --git a/meilisearch-http/src/data/updates.rs b/meilisearch-http/src/data/updates.rs index 518f9fcc3..23949aa86 100644 --- a/meilisearch-http/src/data/updates.rs +++ b/meilisearch-http/src/data/updates.rs @@ -2,7 +2,7 @@ use actix_web::web::Payload; use milli::update::{IndexDocumentsMethod, UpdateFormat}; use super::Data; -use crate::index::Settings; +use crate::index::{Checked, Settings}; use crate::index_controller::{IndexMetadata, IndexSettings, UpdateStatus}; impl Data { @@ -24,7 +24,7 @@ impl Data { pub async fn update_settings( &self, index: String, - settings: Settings, + settings: Settings, create: bool, ) -> anyhow::Result { let update = self diff --git a/meilisearch-http/src/index/mod.rs b/meilisearch-http/src/index/mod.rs index 43c8b0193..c897fac3f 100644 --- a/meilisearch-http/src/index/mod.rs +++ b/meilisearch-http/src/index/mod.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeSet, HashSet}; +use std::{collections::{BTreeSet, HashSet}, marker::PhantomData}; use std::ops::Deref; use std::sync::Arc; @@ -8,7 +8,7 @@ use serde_json::{Map, Value}; use crate::helpers::EnvSizer; pub use search::{SearchQuery, SearchResult, DEFAULT_SEARCH_LIMIT}; -pub use updates::{Facets, Settings}; +pub use updates::{Facets, Settings, Checked, Unchecked}; mod search; mod updates; @@ -27,7 +27,7 @@ impl Deref for Index { } impl Index { - pub fn settings(&self) -> anyhow::Result { + pub fn settings(&self) -> anyhow::Result> { let txn = self.read_txn()?; let displayed_attributes = self @@ -68,6 +68,7 @@ impl Index { ranking_rules: Some(Some(criteria)), stop_words: Some(Some(stop_words)), distinct_attribute: Some(distinct_attribute), + _kind: PhantomData, }) } diff --git a/meilisearch-http/src/index/updates.rs b/meilisearch-http/src/index/updates.rs index 4afd2e297..0d76f2ae6 100644 --- a/meilisearch-http/src/index/updates.rs +++ b/meilisearch-http/src/index/updates.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeSet, HashMap}; use std::io; use std::num::NonZeroUsize; +use std::marker::PhantomData; use flate2::read::GzDecoder; use log::info; @@ -10,10 +11,15 @@ use serde::{de::Deserializer, Deserialize, Serialize}; use super::Index; use crate::index_controller::UpdateResult; +#[derive(Clone, Default, Debug)] +pub struct Checked; +#[derive(Clone, Default, Debug)] +pub struct Unchecked; + #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] -pub struct Settings { +pub struct Settings { #[serde( default, deserialize_with = "deserialize_some", @@ -49,17 +55,57 @@ pub struct Settings { skip_serializing_if = "Option::is_none" )] pub distinct_attribute: Option>, + + #[serde(skip)] + pub _kind: PhantomData, } -impl Settings { - pub fn cleared() -> Self { - Self { +impl Settings { + pub fn cleared() -> Settings { + Settings { displayed_attributes: Some(None), searchable_attributes: Some(None), attributes_for_faceting: Some(None), ranking_rules: Some(None), stop_words: Some(None), distinct_attribute: Some(None), + _kind: PhantomData, + } + } +} + +impl Settings { + pub fn check(mut self) -> Settings { + let displayed_attributes = match self.displayed_attributes.take() { + Some(Some(fields)) => { + if fields.iter().any(|f| f == "*") { + Some(None) + } else { + Some(Some(fields)) + } + } + otherwise => otherwise, + }; + + let searchable_attributes = match self.searchable_attributes.take() { + Some(Some(fields)) => { + if fields.iter().any(|f| f == "*") { + Some(None) + } else { + Some(Some(fields)) + } + } + otherwise => otherwise, + }; + + Settings { + displayed_attributes, + searchable_attributes, + attributes_for_faceting: self.attributes_for_faceting, + ranking_rules: self.ranking_rules, + stop_words: self.stop_words, + distinct_attribute: self.distinct_attribute, + _kind: PhantomData, } } } @@ -137,7 +183,7 @@ impl Index { pub fn update_settings( &self, - settings: &Settings, + settings: &Settings, update_builder: UpdateBuilder, ) -> anyhow::Result { // We must use the write transaction of the update here. @@ -222,3 +268,42 @@ impl Index { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_setting_check() { + // test no changes + let settings = Settings { + displayed_attributes: Some(Some(vec![String::from("hello")])), + searchable_attributes: Some(Some(vec![String::from("hello")])), + attributes_for_faceting: None, + ranking_rules: None, + stop_words: None, + distinct_attribute: None, + _kind: PhantomData::, + }; + + let checked = settings.clone().check(); + assert_eq!(settings.displayed_attributes, checked.displayed_attributes); + assert_eq!(settings.searchable_attributes, checked.searchable_attributes); + + // test wildcard + // test no changes + let settings = Settings { + displayed_attributes: Some(Some(vec![String::from("*")])), + searchable_attributes: Some(Some(vec![String::from("hello"), String::from("*")])), + attributes_for_faceting: None, + ranking_rules: None, + stop_words: None, + distinct_attribute: None, + _kind: PhantomData::, + }; + + let checked = settings.check(); + assert_eq!(checked.displayed_attributes, Some(None)); + assert_eq!(checked.searchable_attributes, Some(None)); + } +} diff --git a/meilisearch-http/src/index_controller/index_actor/actor.rs b/meilisearch-http/src/index_controller/index_actor/actor.rs index 06d2b0f60..1f1cf146b 100644 --- a/meilisearch-http/src/index_controller/index_actor/actor.rs +++ b/meilisearch-http/src/index_controller/index_actor/actor.rs @@ -10,7 +10,7 @@ use tokio::sync::mpsc; use tokio::task::spawn_blocking; use uuid::Uuid; -use crate::index::{Document, SearchQuery, SearchResult, Settings}; +use crate::index::{Checked, Document, SearchQuery, SearchResult, Settings}; use crate::index_controller::{ get_arc_ownership_blocking, update_handler::UpdateHandler, Failed, IndexStats, Processed, Processing, @@ -164,7 +164,7 @@ impl IndexActor { .map_err(|e| IndexError::Error(e.into())) } - async fn handle_settings(&self, uuid: Uuid) -> IndexResult { + async fn handle_settings(&self, uuid: Uuid) -> IndexResult> { let index = self .store .get(uuid) diff --git a/meilisearch-http/src/index_controller/index_actor/handle_impl.rs b/meilisearch-http/src/index_controller/index_actor/handle_impl.rs index 719e12cee..4569ea020 100644 --- a/meilisearch-http/src/index_controller/index_actor/handle_impl.rs +++ b/meilisearch-http/src/index_controller/index_actor/handle_impl.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use tokio::sync::{mpsc, oneshot}; use uuid::Uuid; -use crate::index_controller::{IndexSettings, IndexStats, Processing}; +use crate::{index::Checked, index_controller::{IndexSettings, IndexStats, Processing}}; use crate::{ index::{Document, SearchQuery, SearchResult, Settings}, index_controller::{Failed, Processed}, @@ -57,7 +57,7 @@ impl IndexActorHandle for IndexActorHandleImpl { Ok(receiver.await.expect("IndexActor has been killed")?) } - async fn settings(&self, uuid: Uuid) -> IndexResult { + async fn settings(&self, uuid: Uuid) -> IndexResult> { let (ret, receiver) = oneshot::channel(); let msg = IndexMsg::Settings { uuid, ret }; let _ = self.sender.send(msg).await; diff --git a/meilisearch-http/src/index_controller/index_actor/message.rs b/meilisearch-http/src/index_controller/index_actor/message.rs index d728b2564..4e2824871 100644 --- a/meilisearch-http/src/index_controller/index_actor/message.rs +++ b/meilisearch-http/src/index_controller/index_actor/message.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use tokio::sync::oneshot; use uuid::Uuid; -use crate::index::{Document, SearchQuery, SearchResult, Settings}; +use crate::index::{Document, SearchQuery, SearchResult, Settings, Checked}; use crate::index_controller::{Failed, IndexStats, Processed, Processing}; use super::{IndexMeta, IndexResult, IndexSettings}; @@ -27,7 +27,7 @@ pub enum IndexMsg { }, Settings { uuid: Uuid, - ret: oneshot::Sender>, + ret: oneshot::Sender>>, }, Documents { uuid: Uuid, diff --git a/meilisearch-http/src/index_controller/index_actor/mod.rs b/meilisearch-http/src/index_controller/index_actor/mod.rs index ef5ea524c..f7f230349 100644 --- a/meilisearch-http/src/index_controller/index_actor/mod.rs +++ b/meilisearch-http/src/index_controller/index_actor/mod.rs @@ -14,7 +14,7 @@ pub use handle_impl::IndexActorHandleImpl; use message::IndexMsg; use store::{IndexStore, MapIndexStore}; -use crate::index::{Document, Index, SearchQuery, SearchResult, Settings}; +use crate::index::{Checked, Document, Index, SearchQuery, SearchResult, Settings}; use crate::index_controller::{Failed, Processed, Processing, IndexStats}; use super::IndexSettings; @@ -74,7 +74,7 @@ pub trait IndexActorHandle { data: Option, ) -> anyhow::Result>; async fn search(&self, uuid: Uuid, query: SearchQuery) -> IndexResult; - async fn settings(&self, uuid: Uuid) -> IndexResult; + async fn settings(&self, uuid: Uuid) -> IndexResult>; async fn documents( &self, @@ -130,7 +130,7 @@ mod test { self.as_ref().search(uuid, query).await } - async fn settings(&self, uuid: Uuid) -> IndexResult { + async fn settings(&self, uuid: Uuid) -> IndexResult> { self.as_ref().settings(uuid).await } diff --git a/meilisearch-http/src/index_controller/mod.rs b/meilisearch-http/src/index_controller/mod.rs index 81e6d0a5e..f1da36740 100644 --- a/meilisearch-http/src/index_controller/mod.rs +++ b/meilisearch-http/src/index_controller/mod.rs @@ -20,7 +20,7 @@ use snapshot::{SnapshotService, load_snapshot}; use update_actor::UpdateActorHandle; use uuid_resolver::{UuidError, UuidResolverHandle}; -use crate::index::{Settings, Document, SearchQuery, SearchResult}; +use crate::index::{Checked, Document, SearchQuery, SearchResult, Settings}; use crate::option::Opt; mod index_actor; @@ -202,7 +202,7 @@ impl IndexController { pub async fn update_settings( &self, uid: String, - settings: Settings, + settings: Settings, create: bool, ) -> anyhow::Result { let perform_udpate = |uuid| async move { @@ -282,7 +282,7 @@ impl IndexController { Ok(ret) } - pub async fn settings(&self, uid: String) -> anyhow::Result { + pub async fn settings(&self, uid: String) -> anyhow::Result> { let uuid = self.uuid_resolver.get(uid.clone()).await?; let settings = self.index_handle.settings(uuid).await?; Ok(settings) diff --git a/meilisearch-http/src/index_controller/updates.rs b/meilisearch-http/src/index_controller/updates.rs index 02827abd0..a129a25a0 100644 --- a/meilisearch-http/src/index_controller/updates.rs +++ b/meilisearch-http/src/index_controller/updates.rs @@ -4,7 +4,7 @@ use chrono::{DateTime, Utc}; use milli::update::{DocumentAdditionResult, IndexDocumentsMethod, UpdateFormat}; use serde::{Deserialize, Serialize}; -use crate::index::Settings; +use crate::index::{Checked, Settings}; pub type UpdateError = String; @@ -25,7 +25,7 @@ pub enum UpdateMeta { }, ClearDocuments, DeleteDocuments, - Settings(Settings), + Settings(Settings), } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/meilisearch-http/src/routes/settings/mod.rs b/meilisearch-http/src/routes/settings/mod.rs index 236c94fed..03f1ee95c 100644 --- a/meilisearch-http/src/routes/settings/mod.rs +++ b/meilisearch-http/src/routes/settings/mod.rs @@ -1,6 +1,6 @@ use actix_web::{delete, get, post, web, HttpResponse}; -use crate::error::ResponseError; +use crate::{error::ResponseError, index::Unchecked}; use crate::helpers::Authentication; use crate::index::Settings; use crate::Data; @@ -138,10 +138,11 @@ create_services!( async fn update_all( data: web::Data, index_uid: web::Path, - body: web::Json, + body: web::Json>, ) -> Result { + let settings = body.into_inner().check(); match data - .update_settings(index_uid.into_inner(), body.into_inner(), true) + .update_settings(index_uid.into_inner(), settings, true) .await { Ok(update_result) => Ok(