196: Implements the synonyms in transplant r=MarinPostma a=irevoire

closes #18 

Co-authored-by: Tamo <tamo@meilisearch.com>
Co-authored-by: marin postma <postma.marin@protonmail.com>
This commit is contained in:
bors[bot] 2021-06-14 14:09:08 +00:00 committed by GitHub
commit 13e864d29f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 57 deletions

View File

@ -84,6 +84,19 @@ impl Index {
.unwrap_or_else(BTreeSet::new); .unwrap_or_else(BTreeSet::new);
let distinct_field = self.distinct_field(&txn)?.map(String::from); let distinct_field = self.distinct_field(&txn)?.map(String::from);
// in milli each word in the synonyms map were split on their separator. Since we lost
// this information we are going to put space between words.
let synonyms = self
.synonyms(&txn)?
.iter()
.map(|(key, values)| {
(
key.join(" "),
values.iter().map(|value| value.join(" ")).collect(),
)
})
.collect();
Ok(Settings { Ok(Settings {
displayed_attributes: Some(displayed_attributes), displayed_attributes: Some(displayed_attributes),
searchable_attributes: Some(searchable_attributes), searchable_attributes: Some(searchable_attributes),
@ -91,6 +104,7 @@ impl Index {
ranking_rules: Some(Some(criteria)), ranking_rules: Some(Some(criteria)),
stop_words: Some(Some(stop_words)), stop_words: Some(Some(stop_words)),
distinct_attribute: Some(distinct_field), distinct_attribute: Some(distinct_field),
synonyms: Some(Some(synonyms)),
_kind: PhantomData, _kind: PhantomData,
}) })
} }

View File

@ -1,4 +1,4 @@
use std::collections::{BTreeSet, HashSet}; use std::collections::{BTreeSet, BTreeMap, HashSet};
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
@ -70,6 +70,12 @@ pub struct Settings<T> {
deserialize_with = "deserialize_some", deserialize_with = "deserialize_some",
skip_serializing_if = "Option::is_none" skip_serializing_if = "Option::is_none"
)] )]
pub synonyms: Option<Option<BTreeMap<String, Vec<String>>>>,
#[serde(
default,
deserialize_with = "deserialize_some",
skip_serializing_if = "Option::is_none"
)]
pub distinct_attribute: Option<Option<String>>, pub distinct_attribute: Option<Option<String>>,
#[serde(skip)] #[serde(skip)]
@ -84,6 +90,7 @@ impl Settings<Checked> {
filterable_attributes: Some(None), filterable_attributes: Some(None),
ranking_rules: Some(None), ranking_rules: Some(None),
stop_words: Some(None), stop_words: Some(None),
synonyms: Some(None),
distinct_attribute: Some(None), distinct_attribute: Some(None),
_kind: PhantomData, _kind: PhantomData,
} }
@ -96,6 +103,7 @@ impl Settings<Checked> {
filterable_attributes, filterable_attributes,
ranking_rules, ranking_rules,
stop_words, stop_words,
synonyms,
distinct_attribute, distinct_attribute,
.. ..
} = self; } = self;
@ -106,6 +114,7 @@ impl Settings<Checked> {
filterable_attributes, filterable_attributes,
ranking_rules, ranking_rules,
stop_words, stop_words,
synonyms,
distinct_attribute, distinct_attribute,
_kind: PhantomData, _kind: PhantomData,
} }
@ -142,6 +151,7 @@ impl Settings<Unchecked> {
filterable_attributes: self.filterable_attributes, filterable_attributes: self.filterable_attributes,
ranking_rules: self.ranking_rules, ranking_rules: self.ranking_rules,
stop_words: self.stop_words, stop_words: self.stop_words,
synonyms: self.synonyms,
distinct_attribute: self.distinct_attribute, distinct_attribute: self.distinct_attribute,
_kind: PhantomData, _kind: PhantomData,
} }
@ -267,7 +277,14 @@ impl Index {
if let Some(ref stop_words) = settings.stop_words { if let Some(ref stop_words) = settings.stop_words {
match stop_words { match stop_words {
Some(stop_words) => builder.set_stop_words(stop_words.clone()), Some(stop_words) => builder.set_stop_words(stop_words.clone()),
_ => builder.reset_stop_words(), None => builder.reset_stop_words(),
}
}
if let Some(ref synonyms) = settings.synonyms {
match synonyms {
Some(synonyms) => builder.set_synonyms(synonyms.clone().into_iter().collect()),
None => builder.reset_synonyms(),
} }
} }
@ -332,6 +349,7 @@ mod test {
filterable_attributes: None, filterable_attributes: None,
ranking_rules: None, ranking_rules: None,
stop_words: None, stop_words: None,
synonyms: None,
distinct_attribute: None, distinct_attribute: None,
_kind: PhantomData::<Unchecked>, _kind: PhantomData::<Unchecked>,
}; };
@ -351,6 +369,7 @@ mod test {
filterable_attributes: None, filterable_attributes: None,
ranking_rules: None, ranking_rules: None,
stop_words: None, stop_words: None,
synonyms: None,
distinct_attribute: None, distinct_attribute: None,
_kind: PhantomData::<Unchecked>, _kind: PhantomData::<Unchecked>,
}; };

View File

@ -133,9 +133,6 @@ fn load_index(
/// we need to **always** be able to convert the old settings to the settings currently being used /// we need to **always** be able to convert the old settings to the settings currently being used
impl From<Settings> for index_controller::Settings<Unchecked> { impl From<Settings> for index_controller::Settings<Unchecked> {
fn from(settings: Settings) -> Self { fn from(settings: Settings) -> Self {
if settings.synonyms.flatten().is_some() {
error!("`synonyms` are not yet implemented and thus will be ignored");
}
Self { Self {
distinct_attribute: settings.distinct_attribute, distinct_attribute: settings.distinct_attribute,
// we need to convert the old `Vec<String>` into a `BTreeSet<String>` // we need to convert the old `Vec<String>` into a `BTreeSet<String>`
@ -167,6 +164,8 @@ impl From<Settings> for index_controller::Settings<Unchecked> {
}).collect())), }).collect())),
// we need to convert the old `Vec<String>` into a `BTreeSet<String>` // we need to convert the old `Vec<String>` into a `BTreeSet<String>`
stop_words: settings.stop_words.map(|o| o.map(|vec| vec.into_iter().collect())), stop_words: settings.stop_words.map(|o| o.map(|vec| vec.into_iter().collect())),
// we need to convert the old `Vec<String>` into a `BTreeMap<String>`
synonyms: settings.synonyms.map(|o| o.map(|vec| vec.into_iter().collect())),
_kind: PhantomData, _kind: PhantomData,
} }
} }

View File

@ -14,6 +14,7 @@ pub enum UpdateResult {
Other, Other,
} }
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum UpdateMeta { pub enum UpdateMeta {

View File

@ -43,7 +43,6 @@ macro_rules! create_app {
.configure(index::services) .configure(index::services)
.configure(search::services) .configure(search::services)
.configure(settings::services) .configure(settings::services)
.configure(synonym::services)
.configure(health::services) .configure(health::services)
.configure(stats::services) .configure(stats::services)
.configure(key::services) .configure(key::services)

View File

@ -15,9 +15,9 @@ pub mod key;
pub mod search; pub mod search;
pub mod settings; pub mod settings;
pub mod stats; pub mod stats;
pub mod synonym;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::clippy::large_enum_variant)]
#[serde(tag = "name")] #[serde(tag = "name")]
pub enum UpdateType { pub enum UpdateType {
ClearAll, ClearAll,

View File

@ -97,6 +97,12 @@ make_setting_route!(
stop_words stop_words
); );
make_setting_route!(
"/indexes/{index_uid}/settings/synonyms",
std::collections::BTreeMap<String, Vec<String>>,
synonyms
);
make_setting_route!( make_setting_route!(
"/indexes/{index_uid}/settings/distinct-attribute", "/indexes/{index_uid}/settings/distinct-attribute",
String, String,
@ -131,6 +137,7 @@ create_services!(
searchable_attributes, searchable_attributes,
distinct_attribute, distinct_attribute,
stop_words, stop_words,
synonyms,
ranking_rules ranking_rules
); );

View File

@ -1,47 +0,0 @@
use std::collections::BTreeMap;
use actix_web::{delete, get, post};
use actix_web::{web, HttpResponse};
use crate::error::ResponseError;
use crate::helpers::Authentication;
use crate::routes::IndexParam;
use crate::Data;
pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(get).service(update).service(delete);
}
#[get(
"/indexes/{index_uid}/settings/synonyms",
wrap = "Authentication::Private"
)]
async fn get(
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[post(
"/indexes/{index_uid}/settings/synonyms",
wrap = "Authentication::Private"
)]
async fn update(
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_body: web::Json<BTreeMap<String, Vec<String>>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[delete(
"/indexes/{index_uid}/settings/synonyms",
wrap = "Authentication::Private"
)]
async fn delete(
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}

View File

@ -16,7 +16,7 @@ async fn get_settings() {
let (response, code) = index.settings().await; let (response, code) = index.settings().await;
assert_eq!(code, 200); assert_eq!(code, 200);
let settings = response.as_object().unwrap(); let settings = response.as_object().unwrap();
assert_eq!(settings.keys().len(), 6); assert_eq!(settings.keys().len(), 7);
assert_eq!(settings["displayedAttributes"], json!(["*"])); assert_eq!(settings["displayedAttributes"], json!(["*"]));
assert_eq!(settings["searchableAttributes"], json!(["*"])); assert_eq!(settings["searchableAttributes"], json!(["*"]));
assert_eq!(settings["filterableAttributes"], json!([])); assert_eq!(settings["filterableAttributes"], json!([]));
@ -87,7 +87,7 @@ async fn reset_all_settings() {
index.wait_update_id(0).await; index.wait_update_id(0).await;
index index
.update_settings(json!({"displayedAttributes": ["name", "age"], "searchableAttributes": ["name"], "stopWords": ["the"], "filterableAttributes": ["age"] })) .update_settings(json!({"displayedAttributes": ["name", "age"], "searchableAttributes": ["name"], "stopWords": ["the"], "filterableAttributes": ["age"], "synonyms": {"puppy": ["dog", "doggo", "potat"] }}))
.await; .await;
index.wait_update_id(1).await; index.wait_update_id(1).await;
let (response, code) = index.settings().await; let (response, code) = index.settings().await;
@ -95,6 +95,10 @@ async fn reset_all_settings() {
assert_eq!(response["displayedAttributes"], json!(["name", "age"])); assert_eq!(response["displayedAttributes"], json!(["name", "age"]));
assert_eq!(response["searchableAttributes"], json!(["name"])); assert_eq!(response["searchableAttributes"], json!(["name"]));
assert_eq!(response["stopWords"], json!(["the"])); assert_eq!(response["stopWords"], json!(["the"]));
assert_eq!(
response["synonyms"],
json!({"puppy": ["dog", "doggo", "potat"] })
);
assert_eq!(response["filterableAttributes"], json!(["age"])); assert_eq!(response["filterableAttributes"], json!(["age"]));
index.delete_settings().await; index.delete_settings().await;
@ -106,6 +110,7 @@ async fn reset_all_settings() {
assert_eq!(response["searchableAttributes"], json!(["*"])); assert_eq!(response["searchableAttributes"], json!(["*"]));
assert_eq!(response["stopWords"], json!([])); assert_eq!(response["stopWords"], json!([]));
assert_eq!(response["filterableAttributes"], json!([])); assert_eq!(response["filterableAttributes"], json!([]));
assert_eq!(response["synonyms"], json!({}));
let (response, code) = index.get_document(1, None).await; let (response, code) = index.get_document(1, None).await;
assert_eq!(code, 200); assert_eq!(code, 200);
@ -184,5 +189,6 @@ test_setting_routes!(
filterable_attributes, filterable_attributes,
displayed_attributes, displayed_attributes,
searchable_attributes, searchable_attributes,
stop_words stop_words,
synonyms
); );