mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-09-04 11:46:30 +00:00
Add two other "did you mean" messages
This commit is contained in:
@ -159,10 +159,8 @@ fn parse_vectors(input: Span) -> IResult<(Token, Option<Token>, VectorFilter<'_>
|
|||||||
|
|
||||||
if let Ok((input, point)) = tag::<_, _, ()>(".")(input) {
|
if let Ok((input, point)) = tag::<_, _, ()>(".")(input) {
|
||||||
let opt_value = parse_vector_value(input).ok().map(|(_, v)| v);
|
let opt_value = parse_vector_value(input).ok().map(|(_, v)| v);
|
||||||
let value = opt_value
|
let value =
|
||||||
.as_ref()
|
opt_value.as_ref().map(|v| v.value().to_owned()).unwrap_or_else(|| point.to_string());
|
||||||
.map(|v| v.original_span().to_string())
|
|
||||||
.unwrap_or_else(|| point.to_string());
|
|
||||||
let context = opt_value.map(|v| v.original_span()).unwrap_or(point);
|
let context = opt_value.map(|v| v.original_span()).unwrap_or(point);
|
||||||
return Err(Error::failure_from_kind(context, ErrorKind::VectorFilterUnknownSuffix(value)));
|
return Err(Error::failure_from_kind(context, ErrorKind::VectorFilterUnknownSuffix(value)));
|
||||||
}
|
}
|
||||||
|
@ -952,13 +952,13 @@ async fn vector_filter_non_existant_fragment() {
|
|||||||
|
|
||||||
let (value, _code) = index
|
let (value, _code) = index
|
||||||
.search_post(json!({
|
.search_post(json!({
|
||||||
"filter": "_vectors.rest.fragments.other EXISTS",
|
"filter": "_vectors.rest.fragments.withBred EXISTS",
|
||||||
"attributesToRetrieve": ["name"]
|
"attributesToRetrieve": ["name"]
|
||||||
}))
|
}))
|
||||||
.await;
|
.await;
|
||||||
snapshot!(value, @r#"
|
snapshot!(value, @r#"
|
||||||
{
|
{
|
||||||
"message": "Index `[uuid]`: The fragment `other` does not exist on embedder `rest`. Available fragments on this embedder are: `basic`, `withBreed`.\n25:30 _vectors.rest.fragments.other EXISTS",
|
"message": "Index `[uuid]`: The fragment `withBred` does not exist on embedder `rest`. Available fragments on this embedder are: `basic`, `withBreed`. Did you mean `withBreed`?\n25:33 _vectors.rest.fragments.withBred EXISTS",
|
||||||
"code": "invalid_search_filter",
|
"code": "invalid_search_filter",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||||
|
@ -639,3 +639,29 @@ fn conditionally_lookup_for_error_message() {
|
|||||||
assert_eq!(err.to_string(), format!("{} {}", prefix, suffix));
|
assert_eq!(err.to_string(), format!("{} {}", prefix, suffix));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DidYouMean<'a>(Option<&'a str>);
|
||||||
|
|
||||||
|
impl<'a> DidYouMean<'a> {
|
||||||
|
pub fn new(key: &str, keys: &'a [String]) -> DidYouMean<'a> {
|
||||||
|
let typos = levenshtein_automata::LevenshteinAutomatonBuilder::new(2, true).build_dfa(key);
|
||||||
|
for key in keys.iter() {
|
||||||
|
match typos.eval(key) {
|
||||||
|
levenshtein_automata::Distance::Exact(_) => {
|
||||||
|
return DidYouMean(Some(key));
|
||||||
|
}
|
||||||
|
levenshtein_automata::Distance::AtLeast(_) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DidYouMean(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for DidYouMean<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(suggestion) = self.0 {
|
||||||
|
write!(f, " Did you mean `{suggestion}`?")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use filter_parser::{Token, VectorFilter};
|
use filter_parser::{Token, VectorFilter};
|
||||||
use roaring::{MultiOps, RoaringBitmap};
|
use roaring::{MultiOps, RoaringBitmap};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::{DidYouMean, Error};
|
||||||
use crate::vector::db::IndexEmbeddingConfig;
|
use crate::vector::db::IndexEmbeddingConfig;
|
||||||
use crate::vector::{ArroyStats, ArroyWrapper};
|
use crate::vector::{ArroyStats, ArroyWrapper};
|
||||||
use crate::Index;
|
use crate::Index;
|
||||||
@ -14,7 +14,8 @@ pub enum VectorFilterError<'a> {
|
|||||||
} else {
|
} else {
|
||||||
let mut available = available.clone();
|
let mut available = available.clone();
|
||||||
available.sort_unstable();
|
available.sort_unstable();
|
||||||
format!("Available embedders are: {}.", available.iter().map(|e| format!("`{e}`")).collect::<Vec<_>>().join(", "))
|
let did_you_mean = DidYouMean::new(embedder.value(), &available);
|
||||||
|
format!("Available embedders are: {}.{did_you_mean}", available.iter().map(|e| format!("`{e}`")).collect::<Vec<_>>().join(", "))
|
||||||
}
|
}
|
||||||
})]
|
})]
|
||||||
EmbedderDoesNotExist { embedder: &'a Token<'a>, available: Vec<String> },
|
EmbedderDoesNotExist { embedder: &'a Token<'a>, available: Vec<String> },
|
||||||
@ -25,7 +26,8 @@ pub enum VectorFilterError<'a> {
|
|||||||
} else {
|
} else {
|
||||||
let mut available = available.clone();
|
let mut available = available.clone();
|
||||||
available.sort_unstable();
|
available.sort_unstable();
|
||||||
format!("Available fragments on this embedder are: {}.", available.iter().map(|f| format!("`{f}`")).collect::<Vec<_>>().join(", "))
|
let did_you_mean = DidYouMean::new(fragment.value(), &available);
|
||||||
|
format!("Available fragments on this embedder are: {}.{did_you_mean}", available.iter().map(|f| format!("`{f}`")).collect::<Vec<_>>().join(", "))
|
||||||
}
|
}
|
||||||
})]
|
})]
|
||||||
FragmentDoesNotExist {
|
FragmentDoesNotExist {
|
||||||
|
Reference in New Issue
Block a user