mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-07-04 12:27:13 +02:00
Rely on FieldIdMapWithMetadata in facet search and filters
This commit is contained in:
parent
67f7470c83
commit
b88aa9cc76
8 changed files with 128 additions and 105 deletions
|
@ -12,17 +12,15 @@ use serde_json::Value;
|
|||
use super::facet_range_search;
|
||||
use crate::constants::RESERVED_GEO_FIELD_NAME;
|
||||
use crate::error::{Error, UserError};
|
||||
use crate::filterable_attributes_rules::{
|
||||
filtered_matching_field_names, is_field_filterable, matching_features,
|
||||
};
|
||||
use crate::fields_ids_map::metadata::FieldIdMapWithMetadata;
|
||||
use crate::filterable_attributes_rules::{filtered_matching_field_names, is_field_filterable};
|
||||
use crate::heed_codec::facet::{
|
||||
FacetGroupKey, FacetGroupKeyCodec, FacetGroupValue, FacetGroupValueCodec, OrderedF64Codec,
|
||||
};
|
||||
use crate::index::db_name::FACET_ID_STRING_DOCIDS;
|
||||
use crate::{
|
||||
distance_between_two_points, lat_lng_to_xyz, FieldId, FieldsIdsMap,
|
||||
FilterableAttributesFeatures, FilterableAttributesRule, Index, InternalError, Result,
|
||||
SerializationError,
|
||||
distance_between_two_points, lat_lng_to_xyz, FieldId, FilterableAttributesFeatures,
|
||||
FilterableAttributesRule, Index, InternalError, Result, SerializationError,
|
||||
};
|
||||
|
||||
/// The maximum number of filters the filter AST can process.
|
||||
|
@ -234,21 +232,32 @@ impl<'a> Filter<'a> {
|
|||
impl<'a> Filter<'a> {
|
||||
pub fn evaluate(&self, rtxn: &heed::RoTxn<'_>, index: &Index) -> Result<RoaringBitmap> {
|
||||
// to avoid doing this for each recursive call we're going to do it ONCE ahead of time
|
||||
let fields_ids_map = index.fields_ids_map(rtxn)?;
|
||||
let fields_ids_map = index.fields_ids_map_with_metadata(rtxn)?;
|
||||
let filterable_attributes_rules = index.filterable_attributes_rules(rtxn)?;
|
||||
for fid in self.condition.fids(MAX_FILTER_DEPTH) {
|
||||
let attribute = fid.value();
|
||||
if !is_field_filterable(attribute, &filterable_attributes_rules) {
|
||||
return Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
||||
attribute,
|
||||
filterable_fields: filtered_matching_field_names(
|
||||
&filterable_attributes_rules,
|
||||
&fields_ids_map,
|
||||
&|features| features.is_filterable(),
|
||||
),
|
||||
}))?;
|
||||
if let Some((_, metadata)) = fields_ids_map.id_with_metadata(fid.value()) {
|
||||
if metadata
|
||||
.filterable_attributes_features(&filterable_attributes_rules)
|
||||
.is_filterable()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
} else if is_field_filterable(attribute, &filterable_attributes_rules) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the field is not filterable, return an error
|
||||
return Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
||||
attribute,
|
||||
filterable_fields: filtered_matching_field_names(
|
||||
&filterable_attributes_rules,
|
||||
&fields_ids_map,
|
||||
&|features| features.is_filterable(),
|
||||
),
|
||||
}))?;
|
||||
}
|
||||
|
||||
self.inner_evaluate(rtxn, index, &fields_ids_map, &filterable_attributes_rules, None)
|
||||
}
|
||||
|
||||
|
@ -259,7 +268,7 @@ impl<'a> Filter<'a> {
|
|||
universe: Option<&RoaringBitmap>,
|
||||
operator: &Condition<'a>,
|
||||
features: &FilterableAttributesFeatures,
|
||||
rule_index: usize,
|
||||
rule_index: Option<usize>,
|
||||
) -> Result<RoaringBitmap> {
|
||||
let numbers_db = index.facet_id_f64_docids;
|
||||
let strings_db = index.facet_id_string_docids;
|
||||
|
@ -454,7 +463,7 @@ impl<'a> Filter<'a> {
|
|||
&self,
|
||||
rtxn: &heed::RoTxn<'_>,
|
||||
index: &Index,
|
||||
field_ids_map: &FieldsIdsMap,
|
||||
field_ids_map: &FieldIdMapWithMetadata,
|
||||
filterable_attribute_rules: &[FilterableAttributesRule],
|
||||
universe: Option<&RoaringBitmap>,
|
||||
) -> Result<RoaringBitmap> {
|
||||
|
@ -480,51 +489,33 @@ impl<'a> Filter<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
FilterCondition::In { fid, els } => {
|
||||
match matching_features(fid.value(), filterable_attribute_rules) {
|
||||
Some((rule_index, features)) if features.is_filterable() => {
|
||||
if let Some(fid) = field_ids_map.id(fid.value()) {
|
||||
els.iter()
|
||||
.map(|el| Condition::Equal(el.clone()))
|
||||
.map(|op| {
|
||||
Self::evaluate_operator(
|
||||
rtxn, index, fid, universe, &op, &features, rule_index,
|
||||
)
|
||||
})
|
||||
.union()
|
||||
} else {
|
||||
Ok(RoaringBitmap::new())
|
||||
}
|
||||
}
|
||||
_ => Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
||||
attribute: fid.value(),
|
||||
filterable_fields: filtered_matching_field_names(
|
||||
filterable_attribute_rules,
|
||||
&field_ids_map,
|
||||
&|features| features.is_filterable(),
|
||||
),
|
||||
}))?,
|
||||
}
|
||||
}
|
||||
FilterCondition::Condition { fid, op } => {
|
||||
match matching_features(fid.value(), filterable_attribute_rules) {
|
||||
Some((rule_index, features)) if features.is_filterable() => {
|
||||
if let Some(fid) = field_ids_map.id(fid.value()) {
|
||||
FilterCondition::In { fid, els } => match field_ids_map.id_with_metadata(fid.value()) {
|
||||
Some((fid, metadata)) => {
|
||||
let (rule_index, features) = metadata
|
||||
.filterable_attributes_features_with_rule_index(filterable_attribute_rules);
|
||||
els.iter()
|
||||
.map(|el| Condition::Equal(el.clone()))
|
||||
.map(|op| {
|
||||
Self::evaluate_operator(
|
||||
rtxn, index, fid, universe, op, &features, rule_index,
|
||||
rtxn, index, fid, universe, &op, &features, rule_index,
|
||||
)
|
||||
} else {
|
||||
Ok(RoaringBitmap::new())
|
||||
}
|
||||
})
|
||||
.union()
|
||||
}
|
||||
None => Ok(RoaringBitmap::new()),
|
||||
},
|
||||
FilterCondition::Condition { fid, op } => {
|
||||
match field_ids_map.id_with_metadata(fid.value()) {
|
||||
Some((fid, metadata)) => {
|
||||
let (rule_index, features) = metadata
|
||||
.filterable_attributes_features_with_rule_index(
|
||||
filterable_attribute_rules,
|
||||
);
|
||||
Self::evaluate_operator(
|
||||
rtxn, index, fid, universe, op, &features, rule_index,
|
||||
)
|
||||
}
|
||||
_ => Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
||||
attribute: fid.value(),
|
||||
filterable_fields: filtered_matching_field_names(
|
||||
filterable_attribute_rules,
|
||||
&field_ids_map,
|
||||
&|features| features.is_filterable(),
|
||||
),
|
||||
}))?,
|
||||
None => Ok(RoaringBitmap::new()),
|
||||
}
|
||||
}
|
||||
FilterCondition::Or(subfilters) => subfilters
|
||||
|
@ -764,7 +755,7 @@ fn generate_filter_error(
|
|||
field_id: FieldId,
|
||||
operator: &Condition<'_>,
|
||||
features: &FilterableAttributesFeatures,
|
||||
rule_index: usize,
|
||||
rule_index: Option<usize>,
|
||||
) -> Error {
|
||||
match index.fields_ids_map(rtxn) {
|
||||
Ok(fields_ids_map) => {
|
||||
|
|
|
@ -77,30 +77,37 @@ impl<'a> SearchForFacetValues<'a> {
|
|||
let rtxn = self.search_query.rtxn;
|
||||
|
||||
let filterable_attributes_rules = index.filterable_attributes_rules(rtxn)?;
|
||||
if !is_field_facet_searchable(&self.facet, &filterable_attributes_rules) {
|
||||
let fields_ids_map = index.fields_ids_map(rtxn)?;
|
||||
let matching_field_names = filtered_matching_field_names(
|
||||
&filterable_attributes_rules,
|
||||
&fields_ids_map,
|
||||
&|features| features.is_facet_searchable(),
|
||||
);
|
||||
let (valid_fields, hidden_fields) =
|
||||
index.remove_hidden_fields(rtxn, matching_field_names)?;
|
||||
|
||||
return Err(UserError::InvalidFacetSearchFacetName {
|
||||
field: self.facet.clone(),
|
||||
valid_fields,
|
||||
hidden_fields,
|
||||
let fields_ids_map = index.fields_ids_map_with_metadata(rtxn)?;
|
||||
let fid = match fields_ids_map.id_with_metadata(&self.facet) {
|
||||
Some((fid, metadata))
|
||||
if metadata
|
||||
.filterable_attributes_features(&filterable_attributes_rules)
|
||||
.is_facet_searchable() =>
|
||||
{
|
||||
fid
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let fields_ids_map = index.fields_ids_map(rtxn)?;
|
||||
let fid = match fields_ids_map.id(&self.facet) {
|
||||
Some(fid) => fid,
|
||||
// we return an empty list of results when the attribute has been
|
||||
// set as filterable but no document contains this field (yet).
|
||||
None => return Ok(Vec::new()),
|
||||
None if is_field_facet_searchable(&self.facet, &filterable_attributes_rules) => {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
// we return an error when the attribute is not facet searchable
|
||||
_otherwise => {
|
||||
let matching_field_names = filtered_matching_field_names(
|
||||
&filterable_attributes_rules,
|
||||
&fields_ids_map,
|
||||
&|features| features.is_facet_searchable(),
|
||||
);
|
||||
let (valid_fields, hidden_fields) =
|
||||
index.remove_hidden_fields(rtxn, matching_field_names)?;
|
||||
|
||||
return Err(UserError::InvalidFacetSearchFacetName {
|
||||
field: self.facet.clone(),
|
||||
valid_fields,
|
||||
hidden_fields,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
let fst = match self.search_query.index.facet_id_string_fst.get(rtxn, &fid)? {
|
||||
|
|
|
@ -192,7 +192,7 @@ impl<'a> Search<'a> {
|
|||
// check if the distinct field is in the filterable fields
|
||||
if !is_field_filterable(distinct, &filterable_fields) {
|
||||
// if not, remove the hidden fields from the filterable fields to generate the error message
|
||||
let fields_ids_map = ctx.index.fields_ids_map(ctx.txn)?;
|
||||
let fields_ids_map = ctx.index.fields_ids_map_with_metadata(ctx.txn)?;
|
||||
let matching_field_names = filtered_matching_field_names(
|
||||
&filterable_fields,
|
||||
&fields_ids_map,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue