mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-03-13 03:21:39 +01:00
Revert metadata creation when computing the facet-distribution
This commit is contained in:
parent
abef655849
commit
40c5f911fd
@ -434,7 +434,7 @@ async fn search_non_filterable_facets() {
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attribute is `title`.",
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attribute pattern is `title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
@ -445,7 +445,7 @@ async fn search_non_filterable_facets() {
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attribute is `title`.",
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attribute pattern is `title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
@ -468,7 +468,7 @@ async fn search_non_filterable_facets_multiple_filterable() {
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attributes are `genres, title`.",
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attribute patterns are `genres, title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
@ -479,7 +479,7 @@ async fn search_non_filterable_facets_multiple_filterable() {
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attributes are `genres, title`.",
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attribute patterns are `genres, title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
@ -532,7 +532,7 @@ async fn search_non_filterable_facets_multiple_facets() {
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attributes `doggo, neko` are not filterable. The available filterable attributes are `genres, title`.",
|
||||
"message": "Invalid facet distribution, attributes `doggo, neko` are not filterable. The available filterable attribute patterns are `genres, title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
@ -543,7 +543,7 @@ async fn search_non_filterable_facets_multiple_facets() {
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attributes `doggo, neko` are not filterable. The available filterable attributes are `genres, title`.",
|
||||
"message": "Invalid facet distribution, attributes `doggo, neko` are not filterable. The available filterable attribute patterns are `genres, title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
@ -1204,52 +1204,55 @@ async fn search_on_unknown_field_plus_joker() {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn distinct_at_search_time() {
|
||||
let server = Server::new_shared();
|
||||
let index = server.unique_index();
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
let (task, _) = index.create(None).await;
|
||||
index.wait_task(task.uid()).await.succeeded();
|
||||
let (response, _code) =
|
||||
index.add_documents(json!([{"id": 1, "color": "Doggo", "machin": "Action"}]), None).await;
|
||||
index.wait_task(response.uid()).await.succeeded();
|
||||
|
||||
let expected_response = json!({
|
||||
"message": format!("Index `{}`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. This index does not have configured filterable attributes.", index.uid),
|
||||
"code": "invalid_search_distinct",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_distinct"
|
||||
});
|
||||
let (response, code) =
|
||||
index.search_post(json!({"page": 0, "hitsPerPage": 2, "distinct": "doggo.truc"})).await;
|
||||
assert_eq!(response, expected_response);
|
||||
assert_eq!(code, 400);
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(response, @r###"
|
||||
{
|
||||
"message": "Index `test`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. This index does not have configured filterable attributes.",
|
||||
"code": "invalid_search_distinct",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_distinct"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (task, _) = index.update_settings_filterable_attributes(json!(["color", "machin"])).await;
|
||||
index.wait_task(task.uid()).await.succeeded();
|
||||
|
||||
let expected_response = json!({
|
||||
"message": format!("Index `{}`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. Available filterable attributes are: `color, machin`.", index.uid),
|
||||
"code": "invalid_search_distinct",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_distinct"
|
||||
});
|
||||
let (response, code) =
|
||||
index.search_post(json!({"page": 0, "hitsPerPage": 2, "distinct": "doggo.truc"})).await;
|
||||
assert_eq!(response, expected_response);
|
||||
assert_eq!(code, 400);
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(response, @r###"
|
||||
{
|
||||
"message": "Index `test`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. Available filterable attributes patterns are: `color, machin`.",
|
||||
"code": "invalid_search_distinct",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_distinct"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (task, _) = index.update_settings_displayed_attributes(json!(["color"])).await;
|
||||
index.wait_task(task.uid()).await.succeeded();
|
||||
|
||||
let expected_response = json!({
|
||||
"message": format!("Index `{}`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. Available filterable attributes are: `color, <..hidden-attributes>`.", index.uid),
|
||||
"code": "invalid_search_distinct",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_distinct"
|
||||
});
|
||||
let (response, code) =
|
||||
index.search_post(json!({"page": 0, "hitsPerPage": 2, "distinct": "doggo.truc"})).await;
|
||||
assert_eq!(response, expected_response);
|
||||
assert_eq!(code, 400);
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(response, @r###"
|
||||
{
|
||||
"message": "Index `test`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. Available filterable attributes patterns are: `color, <..hidden-attributes>`.",
|
||||
"code": "invalid_search_distinct",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_distinct"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) =
|
||||
index.search_post(json!({"page": 0, "hitsPerPage": 2, "distinct": true})).await;
|
||||
|
@ -3653,7 +3653,7 @@ async fn federation_non_faceted_for_an_index() {
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
insta::assert_json_snapshot!(response, { ".processingTimeMs" => "[time]" }, @r###"
|
||||
{
|
||||
"message": "Inside `.federation.facetsByIndex.fruits-no-name`: Invalid facet distribution, attribute `name` is not filterable. The available filterable attributes are `BOOST, id`.\n - Note: index `fruits-no-name` used in `.queries[1]`",
|
||||
"message": "Inside `.federation.facetsByIndex.fruits-no-name`: Invalid facet distribution, attribute `name` is not filterable. The available filterable attribute patterns are `BOOST, id`.\n - Note: index `fruits-no-name` used in `.queries[1]`",
|
||||
"code": "invalid_multi_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_multi_search_facets"
|
||||
@ -3675,7 +3675,7 @@ async fn federation_non_faceted_for_an_index() {
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
insta::assert_json_snapshot!(response, { ".processingTimeMs" => "[time]" }, @r###"
|
||||
{
|
||||
"message": "Inside `.federation.facetsByIndex.fruits-no-name`: Invalid facet distribution, attribute `name` is not filterable. The available filterable attributes are `BOOST, id`.\n - Note: index `fruits-no-name` is not used in queries",
|
||||
"message": "Inside `.federation.facetsByIndex.fruits-no-name`: Invalid facet distribution, attribute `name` is not filterable. The available filterable attribute patterns are `BOOST, id`.\n - Note: index `fruits-no-name` is not used in queries",
|
||||
"code": "invalid_multi_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_multi_search_facets"
|
||||
|
@ -452,18 +452,19 @@ async fn filter_invalid_attribute_array() {
|
||||
snapshot!(code, @"202 Accepted");
|
||||
index.wait_task(value.uid()).await.succeeded();
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass",
|
||||
"code": "invalid_similar_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_similar_filter"
|
||||
});
|
||||
index
|
||||
.similar(
|
||||
json!({"id": 287947, "filter": ["many = Glass"], "embedder": "manual"}),
|
||||
|response, code| {
|
||||
assert_eq!(response, expected_response);
|
||||
assert_eq!(code, 400);
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(response, @r###"
|
||||
{
|
||||
"message": "Attribute `many` is not filterable. Available filterable attributes patterns are: `title`.\n1:5 many = Glass",
|
||||
"code": "invalid_similar_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_similar_filter"
|
||||
}
|
||||
"###);
|
||||
},
|
||||
)
|
||||
.await;
|
||||
@ -492,18 +493,19 @@ async fn filter_invalid_attribute_string() {
|
||||
snapshot!(code, @"202 Accepted");
|
||||
index.wait_task(value.uid()).await.succeeded();
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass",
|
||||
"code": "invalid_similar_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_similar_filter"
|
||||
});
|
||||
index
|
||||
.similar(
|
||||
json!({"id": 287947, "filter": "many = Glass", "embedder": "manual"}),
|
||||
|response, code| {
|
||||
assert_eq!(response, expected_response);
|
||||
assert_eq!(code, 400);
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(response, @r###"
|
||||
{
|
||||
"message": "Attribute `many` is not filterable. Available filterable attributes patterns are: `title`.\n1:5 many = Glass",
|
||||
"code": "invalid_similar_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_similar_filter"
|
||||
}
|
||||
"###);
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
@ -121,10 +121,10 @@ only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and undersco
|
||||
and can not be more than 511 bytes.", .document_id.to_string()
|
||||
)]
|
||||
InvalidDocumentId { document_id: Value },
|
||||
#[error("Invalid facet distribution, {}", format_invalid_filter_distribution(.invalid_facets_name, .valid_facets_name))]
|
||||
#[error("Invalid facet distribution, {}", format_invalid_filter_distribution(.invalid_facets_name, .valid_patterns))]
|
||||
InvalidFacetsDistribution {
|
||||
invalid_facets_name: BTreeSet<String>,
|
||||
valid_facets_name: BTreeSet<String>,
|
||||
valid_patterns: BTreeSet<String>,
|
||||
},
|
||||
#[error(transparent)]
|
||||
InvalidGeoField(#[from] GeoError),
|
||||
@ -357,9 +357,9 @@ pub enum GeoError {
|
||||
|
||||
fn format_invalid_filter_distribution(
|
||||
invalid_facets_name: &BTreeSet<String>,
|
||||
valid_facets_name: &BTreeSet<String>,
|
||||
valid_patterns: &BTreeSet<String>,
|
||||
) -> String {
|
||||
if valid_facets_name.is_empty() {
|
||||
if valid_patterns.is_empty() {
|
||||
return "this index does not have configured filterable attributes.".into();
|
||||
}
|
||||
|
||||
@ -381,17 +381,17 @@ fn format_invalid_filter_distribution(
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
match valid_facets_name.len() {
|
||||
match valid_patterns.len() {
|
||||
1 => write!(
|
||||
result,
|
||||
" The available filterable attribute is `{}`.",
|
||||
valid_facets_name.first().unwrap()
|
||||
" The available filterable attribute pattern is `{}`.",
|
||||
valid_patterns.first().unwrap()
|
||||
)
|
||||
.unwrap(),
|
||||
_ => write!(
|
||||
result,
|
||||
" The available filterable attributes are `{}`.",
|
||||
valid_facets_name.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", ")
|
||||
" The available filterable attribute patterns are `{}`.",
|
||||
valid_patterns.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", ")
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::attribute_patterns::match_field_legacy;
|
||||
use crate::facet::FacetType;
|
||||
use crate::fields_ids_map::metadata::{FieldIdMapWithMetadata, Metadata, MetadataBuilder};
|
||||
use crate::filterable_attributes_rules::{filtered_matching_patterns, matching_features};
|
||||
use crate::heed_codec::facet::{
|
||||
FacetGroupKeyCodec, FieldDocIdFacetF64Codec, FieldDocIdFacetStringCodec, OrderedF64Codec,
|
||||
};
|
||||
@ -294,13 +294,13 @@ impl<'a> FacetDistribution<'a> {
|
||||
return Ok(Default::default());
|
||||
};
|
||||
|
||||
let fields_ids_map = self.index.fields_ids_map_with_metadata(self.rtxn)?;
|
||||
let fields_ids_map = self.index.fields_ids_map(self.rtxn)?;
|
||||
let filterable_attributes_rules = self.index.filterable_attributes_rules(self.rtxn)?;
|
||||
self.check_faceted_fields(&fields_ids_map, &filterable_attributes_rules)?;
|
||||
self.check_faceted_fields(&filterable_attributes_rules)?;
|
||||
|
||||
let mut distribution = BTreeMap::new();
|
||||
for (fid, name, metadata) in fields_ids_map.iter() {
|
||||
if self.select_field(name, &metadata, &filterable_attributes_rules) {
|
||||
for (fid, name) in fields_ids_map.iter() {
|
||||
if self.select_field(name, &filterable_attributes_rules) {
|
||||
let min_value = if let Some(min_value) = crate::search::facet::facet_min_value(
|
||||
self.index,
|
||||
self.rtxn,
|
||||
@ -331,16 +331,12 @@ impl<'a> FacetDistribution<'a> {
|
||||
|
||||
pub fn execute(&self) -> Result<BTreeMap<String, IndexMap<String, u64>>> {
|
||||
let fields_ids_map = self.index.fields_ids_map(self.rtxn)?;
|
||||
let fields_ids_map = FieldIdMapWithMetadata::new(
|
||||
fields_ids_map,
|
||||
MetadataBuilder::from_index(self.index, self.rtxn)?,
|
||||
);
|
||||
let filterable_attributes_rules = self.index.filterable_attributes_rules(self.rtxn)?;
|
||||
self.check_faceted_fields(&fields_ids_map, &filterable_attributes_rules)?;
|
||||
self.check_faceted_fields(&filterable_attributes_rules)?;
|
||||
|
||||
let mut distribution = BTreeMap::new();
|
||||
for (fid, name, metadata) in fields_ids_map.iter() {
|
||||
if self.select_field(name, &metadata, &filterable_attributes_rules) {
|
||||
for (fid, name) in fields_ids_map.iter() {
|
||||
if self.select_field(name, &filterable_attributes_rules) {
|
||||
let order_by = self
|
||||
.facets
|
||||
.as_ref()
|
||||
@ -358,11 +354,12 @@ impl<'a> FacetDistribution<'a> {
|
||||
fn select_field(
|
||||
&self,
|
||||
name: &str,
|
||||
metadata: &Metadata,
|
||||
filterable_attributes_rules: &[FilterableAttributesRule],
|
||||
) -> bool {
|
||||
// If the field is not filterable, we don't want to compute the facet distribution.
|
||||
if !metadata.filterable_attributes_features(filterable_attributes_rules).is_filterable() {
|
||||
if !matching_features(name, filterable_attributes_rules)
|
||||
.map_or(false, |(_, features)| features.is_filterable())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -378,41 +375,31 @@ impl<'a> FacetDistribution<'a> {
|
||||
/// Check if the fields in the facets are valid faceted fields.
|
||||
fn check_faceted_fields(
|
||||
&self,
|
||||
fields_ids_map: &FieldIdMapWithMetadata,
|
||||
filterable_attributes_rules: &[FilterableAttributesRule],
|
||||
) -> Result<()> {
|
||||
let mut invalid_facets = BTreeSet::new();
|
||||
if let Some(facets) = &self.facets {
|
||||
for field in facets.keys() {
|
||||
let is_valid_faceted_field =
|
||||
fields_ids_map.id_with_metadata(field).map_or(false, |(_, metadata)| {
|
||||
metadata
|
||||
.filterable_attributes_features(filterable_attributes_rules)
|
||||
.is_filterable()
|
||||
});
|
||||
if !is_valid_faceted_field {
|
||||
let is_valid_filterable_field =
|
||||
matching_features(field, filterable_attributes_rules)
|
||||
.map_or(false, |(_, features)| features.is_filterable());
|
||||
if !is_valid_filterable_field {
|
||||
invalid_facets.insert(field.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !invalid_facets.is_empty() {
|
||||
let valid_facets_name = fields_ids_map
|
||||
.iter()
|
||||
.filter_map(|(_, name, metadata)| {
|
||||
if metadata
|
||||
.filterable_attributes_features(filterable_attributes_rules)
|
||||
.is_filterable()
|
||||
{
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let valid_patterns =
|
||||
filtered_matching_patterns(filterable_attributes_rules, &|features| {
|
||||
features.is_filterable()
|
||||
})
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect();
|
||||
return Err(Error::UserError(UserError::InvalidFacetsDistribution {
|
||||
invalid_facets_name: invalid_facets,
|
||||
valid_facets_name,
|
||||
valid_patterns,
|
||||
}));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user