mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-03-06 22:02:34 +08:00
Apply PR requests related to Refactor search and facet-search
This commit is contained in:
parent
63e753bde0
commit
67f7470c83
@ -894,7 +894,7 @@ async fn search_with_pattern_filter_settings_errors() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Filter operator `=` is not allowed for the attribute `cattos`, allowed operators: OR, AND, NOT, <, >, <=, >=, TO, IS EMPTY, IS NULL, EXISTS.",
|
"message": "Index `test`: Filter operator `=` is not allowed for the attribute `cattos`.\n - Note: allowed operators: OR, AND, NOT, <, >, <=, >=, TO, IS EMPTY, IS NULL, EXISTS.\n - Note: field `cattos` matched rule #0 in `filterableAttributes`",
|
||||||
"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"
|
||||||
@ -920,7 +920,7 @@ async fn search_with_pattern_filter_settings_errors() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Filter operator `=` is not allowed for the attribute `cattos`, allowed operators: OR, AND, NOT, <, >, <=, >=, TO, IS EMPTY, IS NULL, EXISTS.",
|
"message": "Index `test`: Filter operator `=` is not allowed for the attribute `cattos`.\n - Note: allowed operators: OR, AND, NOT, <, >, <=, >=, TO, IS EMPTY, IS NULL, EXISTS.\n - Note: field `cattos` matched rule #0 in `filterableAttributes`",
|
||||||
"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"
|
||||||
@ -941,7 +941,7 @@ async fn search_with_pattern_filter_settings_errors() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Filter operator `>` is not allowed for the attribute `doggos.age`, allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.",
|
"message": "Index `test`: Filter operator `>` is not allowed for the attribute `doggos.age`.\n - Note: allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.\n - Note: field `doggos.age` matched rule #0 in `filterableAttributes`",
|
||||||
"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"
|
||||||
@ -967,7 +967,7 @@ async fn search_with_pattern_filter_settings_errors() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Filter operator `>` is not allowed for the attribute `doggos.age`, allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.",
|
"message": "Index `test`: Filter operator `>` is not allowed for the attribute `doggos.age`.\n - Note: allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.\n - Note: field `doggos.age` matched rule #0 in `filterableAttributes`",
|
||||||
"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"
|
||||||
@ -993,7 +993,7 @@ async fn search_with_pattern_filter_settings_errors() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Filter operator `TO` is not allowed for the attribute `doggos.age`, allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.",
|
"message": "Index `test`: Filter operator `TO` is not allowed for the attribute `doggos.age`.\n - Note: allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.\n - Note: field `doggos.age` matched rule #0 in `filterableAttributes`",
|
||||||
"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"
|
||||||
|
@ -335,7 +335,7 @@ async fn search_with_pattern_filter_settings_scenario_1() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Filter operator `>` is not allowed for the attribute `doggos.age`, allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.",
|
"message": "Index `test`: Filter operator `>` is not allowed for the attribute `doggos.age`.\n - Note: allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.\n - Note: field `doggos.age` matched rule #0 in `filterableAttributes`",
|
||||||
"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"
|
||||||
@ -481,7 +481,7 @@ async fn search_with_pattern_filter_settings_scenario_1() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Filter operator `=` is not allowed for the attribute `cattos`, allowed operators: OR, AND, NOT, <, >, <=, >=, TO, IS EMPTY, IS NULL, EXISTS.",
|
"message": "Index `test`: Filter operator `=` is not allowed for the attribute `cattos`.\n - Note: allowed operators: OR, AND, NOT, <, >, <=, >=, TO, IS EMPTY, IS NULL, EXISTS.\n - Note: field `cattos` matched rule #0 in `filterableAttributes`",
|
||||||
"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"
|
||||||
@ -613,7 +613,7 @@ async fn search_with_pattern_filter_settings_scenario_1() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Filter operator `>` is not allowed for the attribute `doggos.age`, allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.",
|
"message": "Index `test`: Filter operator `>` is not allowed for the attribute `doggos.age`.\n - Note: allowed operators: OR, AND, NOT, =, !=, IN, IS EMPTY, IS NULL, EXISTS.\n - Note: field `doggos.age` matched rule #0 in `filterableAttributes`",
|
||||||
"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"
|
||||||
@ -720,7 +720,7 @@ async fn test_filterable_attributes_priority() {
|
|||||||
snapshot!(code, @"400 Bad Request");
|
snapshot!(code, @"400 Bad Request");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
{
|
{
|
||||||
"message": "Index `test`: Attribute `doggos.age` is not filterable. Available filterable attributes are: `doggos.age`, `doggos.name`.\n1:11 doggos.age > 2",
|
"message": "Index `test`: Attribute `doggos.age` is not filterable. Available filterable attributes are: `doggos.name`.\n1:11 doggos.age > 2",
|
||||||
"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"
|
||||||
|
@ -138,8 +138,13 @@ and can not be more than 511 bytes.", .document_id.to_string()
|
|||||||
InvalidFilter(String),
|
InvalidFilter(String),
|
||||||
#[error("Invalid type for filter subexpression: expected: {}, found: {}.", .0.join(", "), .1)]
|
#[error("Invalid type for filter subexpression: expected: {}, found: {}.", .0.join(", "), .1)]
|
||||||
InvalidFilterExpression(&'static [&'static str], Value),
|
InvalidFilterExpression(&'static [&'static str], Value),
|
||||||
#[error("Filter operator `{operator}` is not allowed for the attribute `{field}`, allowed operators: {}.", allowed_operators.join(", "))]
|
#[error("Filter operator `{operator}` is not allowed for the attribute `{field}`.\n - Note: allowed operators: {}.\n - Note: field `{field}` matched rule #{rule_index} in `filterableAttributes`", allowed_operators.join(", "))]
|
||||||
FilterOperatorNotAllowed { field: String, allowed_operators: Vec<String>, operator: String },
|
FilterOperatorNotAllowed {
|
||||||
|
field: String,
|
||||||
|
allowed_operators: Vec<String>,
|
||||||
|
operator: String,
|
||||||
|
rule_index: usize,
|
||||||
|
},
|
||||||
#[error("Attribute `{}` is not sortable. {}",
|
#[error("Attribute `{}` is not sortable. {}",
|
||||||
.field,
|
.field,
|
||||||
match .valid_fields.is_empty() {
|
match .valid_fields.is_empty() {
|
||||||
|
@ -236,19 +236,16 @@ impl Default for FilterFeatures {
|
|||||||
pub fn filtered_matching_field_names<'fim>(
|
pub fn filtered_matching_field_names<'fim>(
|
||||||
filterable_attributes: &[FilterableAttributesRule],
|
filterable_attributes: &[FilterableAttributesRule],
|
||||||
fields_ids_map: &'fim FieldsIdsMap,
|
fields_ids_map: &'fim FieldsIdsMap,
|
||||||
filter: &impl Fn(&FilterableAttributesFeatures) -> bool,
|
filter: &impl Fn(FilterableAttributesFeatures) -> bool,
|
||||||
) -> BTreeSet<&'fim str> {
|
) -> BTreeSet<&'fim str> {
|
||||||
let mut result = BTreeSet::new();
|
let mut result = BTreeSet::new();
|
||||||
for (_, field_name) in fields_ids_map.iter() {
|
for (_, field_name) in fields_ids_map.iter() {
|
||||||
for filterable_attribute in filterable_attributes {
|
if let Some((_, features)) = matching_features(field_name, filterable_attributes) {
|
||||||
if filterable_attribute.match_str(field_name) == PatternMatch::Match {
|
if filter(features) {
|
||||||
let features = filterable_attribute.features();
|
|
||||||
if filter(&features) {
|
|
||||||
result.insert(field_name);
|
result.insert(field_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,13 +257,18 @@ pub fn filtered_matching_field_names<'fim>(
|
|||||||
///
|
///
|
||||||
/// * `field_name` - The field name to match against.
|
/// * `field_name` - The field name to match against.
|
||||||
/// * `filterable_attributes` - The set of filterable attributes rules to match against.
|
/// * `filterable_attributes` - The set of filterable attributes rules to match against.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Some((rule_index, features))` - The features of the matching rule and the index of the rule in the `filterable_attributes` array.
|
||||||
|
/// * `None` - No matching rule was found.
|
||||||
pub fn matching_features(
|
pub fn matching_features(
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
filterable_attributes: &[FilterableAttributesRule],
|
filterable_attributes: &[FilterableAttributesRule],
|
||||||
) -> Option<FilterableAttributesFeatures> {
|
) -> Option<(usize, FilterableAttributesFeatures)> {
|
||||||
for filterable_attribute in filterable_attributes {
|
for (id, filterable_attribute) in filterable_attributes.iter().enumerate() {
|
||||||
if filterable_attribute.match_str(field_name) == PatternMatch::Match {
|
if filterable_attribute.match_str(field_name) == PatternMatch::Match {
|
||||||
return Some(filterable_attribute.features());
|
return Some((id, filterable_attribute.features()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -283,7 +285,7 @@ pub fn is_field_filterable(
|
|||||||
filterable_attributes: &[FilterableAttributesRule],
|
filterable_attributes: &[FilterableAttributesRule],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
matching_features(field_name, filterable_attributes)
|
matching_features(field_name, filterable_attributes)
|
||||||
.map_or(false, |features| features.is_filterable())
|
.map_or(false, |(_, features)| features.is_filterable())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a field is facet searchable calling the method `FilterableAttributesFeatures::is_facet_searchable()`.
|
/// Check if a field is facet searchable calling the method `FilterableAttributesFeatures::is_facet_searchable()`.
|
||||||
@ -297,7 +299,7 @@ pub fn is_field_facet_searchable(
|
|||||||
filterable_attributes: &[FilterableAttributesRule],
|
filterable_attributes: &[FilterableAttributesRule],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
matching_features(field_name, filterable_attributes)
|
matching_features(field_name, filterable_attributes)
|
||||||
.map_or(false, |features| features.is_facet_searchable())
|
.map_or(false, |(_, features)| features.is_facet_searchable())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Match a field against a set of filterable, facet searchable fields, distinct field, sortable fields, and asc_desc fields.
|
/// Match a field against a set of filterable, facet searchable fields, distinct field, sortable fields, and asc_desc fields.
|
||||||
@ -339,7 +341,7 @@ pub fn match_faceted_field(
|
|||||||
fn match_pattern_by_features(
|
fn match_pattern_by_features(
|
||||||
field_name: &str,
|
field_name: &str,
|
||||||
filterable_attributes: &[FilterableAttributesRule],
|
filterable_attributes: &[FilterableAttributesRule],
|
||||||
filter: &impl Fn(&FilterableAttributesFeatures) -> bool,
|
filter: &impl Fn(FilterableAttributesFeatures) -> bool,
|
||||||
) -> PatternMatch {
|
) -> PatternMatch {
|
||||||
let mut selection = PatternMatch::NoMatch;
|
let mut selection = PatternMatch::NoMatch;
|
||||||
|
|
||||||
@ -353,7 +355,7 @@ fn match_pattern_by_features(
|
|||||||
match pattern.match_str(field_name) {
|
match pattern.match_str(field_name) {
|
||||||
PatternMatch::Match => {
|
PatternMatch::Match => {
|
||||||
let features = pattern.features();
|
let features = pattern.features();
|
||||||
if filter(&features) && can_match {
|
if filter(features) && can_match {
|
||||||
return PatternMatch::Match;
|
return PatternMatch::Match;
|
||||||
} else {
|
} else {
|
||||||
can_match = false;
|
can_match = false;
|
||||||
@ -361,7 +363,7 @@ fn match_pattern_by_features(
|
|||||||
}
|
}
|
||||||
PatternMatch::Parent => {
|
PatternMatch::Parent => {
|
||||||
let features = pattern.features();
|
let features = pattern.features();
|
||||||
if filter(&features) {
|
if filter(features) {
|
||||||
selection = PatternMatch::Parent;
|
selection = PatternMatch::Parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,9 @@ use crate::heed_codec::facet::{
|
|||||||
};
|
};
|
||||||
use crate::index::db_name::FACET_ID_STRING_DOCIDS;
|
use crate::index::db_name::FACET_ID_STRING_DOCIDS;
|
||||||
use crate::{
|
use crate::{
|
||||||
distance_between_two_points, lat_lng_to_xyz, FieldId, FilterableAttributesFeatures,
|
distance_between_two_points, lat_lng_to_xyz, FieldId, FieldsIdsMap,
|
||||||
FilterableAttributesRule, Index, InternalError, Result, SerializationError,
|
FilterableAttributesFeatures, FilterableAttributesRule, Index, InternalError, Result,
|
||||||
|
SerializationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The maximum number of filters the filter AST can process.
|
/// The maximum number of filters the filter AST can process.
|
||||||
@ -233,11 +234,11 @@ impl<'a> Filter<'a> {
|
|||||||
impl<'a> Filter<'a> {
|
impl<'a> Filter<'a> {
|
||||||
pub fn evaluate(&self, rtxn: &heed::RoTxn<'_>, index: &Index) -> Result<RoaringBitmap> {
|
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
|
// 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 filterable_attributes_rules = index.filterable_attributes_rules(rtxn)?;
|
let filterable_attributes_rules = index.filterable_attributes_rules(rtxn)?;
|
||||||
for fid in self.condition.fids(MAX_FILTER_DEPTH) {
|
for fid in self.condition.fids(MAX_FILTER_DEPTH) {
|
||||||
let attribute = fid.value();
|
let attribute = fid.value();
|
||||||
if !is_field_filterable(attribute, &filterable_attributes_rules) {
|
if !is_field_filterable(attribute, &filterable_attributes_rules) {
|
||||||
let fields_ids_map = index.fields_ids_map(rtxn)?;
|
|
||||||
return Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
return Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
||||||
attribute,
|
attribute,
|
||||||
filterable_fields: filtered_matching_field_names(
|
filterable_fields: filtered_matching_field_names(
|
||||||
@ -248,7 +249,7 @@ impl<'a> Filter<'a> {
|
|||||||
}))?;
|
}))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.inner_evaluate(rtxn, index, &filterable_attributes_rules, None)
|
self.inner_evaluate(rtxn, index, &fields_ids_map, &filterable_attributes_rules, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_operator(
|
fn evaluate_operator(
|
||||||
@ -258,6 +259,7 @@ impl<'a> Filter<'a> {
|
|||||||
universe: Option<&RoaringBitmap>,
|
universe: Option<&RoaringBitmap>,
|
||||||
operator: &Condition<'a>,
|
operator: &Condition<'a>,
|
||||||
features: &FilterableAttributesFeatures,
|
features: &FilterableAttributesFeatures,
|
||||||
|
rule_index: usize,
|
||||||
) -> Result<RoaringBitmap> {
|
) -> Result<RoaringBitmap> {
|
||||||
let numbers_db = index.facet_id_f64_docids;
|
let numbers_db = index.facet_id_f64_docids;
|
||||||
let strings_db = index.facet_id_string_docids;
|
let strings_db = index.facet_id_string_docids;
|
||||||
@ -275,19 +277,29 @@ impl<'a> Filter<'a> {
|
|||||||
| Condition::Between { .. }
|
| Condition::Between { .. }
|
||||||
if !features.is_filterable_comparison() =>
|
if !features.is_filterable_comparison() =>
|
||||||
{
|
{
|
||||||
return Err(generate_filter_error(rtxn, index, field_id, operator, features));
|
return Err(generate_filter_error(
|
||||||
|
rtxn, index, field_id, operator, features, rule_index,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Condition::Empty if !features.is_filterable_empty() => {
|
Condition::Empty if !features.is_filterable_empty() => {
|
||||||
return Err(generate_filter_error(rtxn, index, field_id, operator, features));
|
return Err(generate_filter_error(
|
||||||
|
rtxn, index, field_id, operator, features, rule_index,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Condition::Null if !features.is_filterable_null() => {
|
Condition::Null if !features.is_filterable_null() => {
|
||||||
return Err(generate_filter_error(rtxn, index, field_id, operator, features));
|
return Err(generate_filter_error(
|
||||||
|
rtxn, index, field_id, operator, features, rule_index,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Condition::Exists if !features.is_filterable_exists() => {
|
Condition::Exists if !features.is_filterable_exists() => {
|
||||||
return Err(generate_filter_error(rtxn, index, field_id, operator, features));
|
return Err(generate_filter_error(
|
||||||
|
rtxn, index, field_id, operator, features, rule_index,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Condition::Equal(_) | Condition::NotEqual(_) if !features.is_filterable_equality() => {
|
Condition::Equal(_) | Condition::NotEqual(_) if !features.is_filterable_equality() => {
|
||||||
return Err(generate_filter_error(rtxn, index, field_id, operator, features));
|
return Err(generate_filter_error(
|
||||||
|
rtxn, index, field_id, operator, features, rule_index,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Condition::GreaterThan(val) => {
|
Condition::GreaterThan(val) => {
|
||||||
(Excluded(val.parse_finite_float()?), Included(f64::MAX))
|
(Excluded(val.parse_finite_float()?), Included(f64::MAX))
|
||||||
@ -338,8 +350,9 @@ impl<'a> Filter<'a> {
|
|||||||
}
|
}
|
||||||
Condition::NotEqual(val) => {
|
Condition::NotEqual(val) => {
|
||||||
let operator = Condition::Equal(val.clone());
|
let operator = Condition::Equal(val.clone());
|
||||||
let docids =
|
let docids = Self::evaluate_operator(
|
||||||
Self::evaluate_operator(rtxn, index, field_id, None, &operator, features)?;
|
rtxn, index, field_id, None, &operator, features, rule_index,
|
||||||
|
)?;
|
||||||
let all_ids = index.documents_ids(rtxn)?;
|
let all_ids = index.documents_ids(rtxn)?;
|
||||||
return Ok(all_ids - docids);
|
return Ok(all_ids - docids);
|
||||||
}
|
}
|
||||||
@ -441,7 +454,8 @@ impl<'a> Filter<'a> {
|
|||||||
&self,
|
&self,
|
||||||
rtxn: &heed::RoTxn<'_>,
|
rtxn: &heed::RoTxn<'_>,
|
||||||
index: &Index,
|
index: &Index,
|
||||||
filterable_fields: &[FilterableAttributesRule],
|
field_ids_map: &FieldsIdsMap,
|
||||||
|
filterable_attribute_rules: &[FilterableAttributesRule],
|
||||||
universe: Option<&RoaringBitmap>,
|
universe: Option<&RoaringBitmap>,
|
||||||
) -> Result<RoaringBitmap> {
|
) -> Result<RoaringBitmap> {
|
||||||
if universe.map_or(false, |u| u.is_empty()) {
|
if universe.map_or(false, |u| u.is_empty()) {
|
||||||
@ -454,7 +468,8 @@ impl<'a> Filter<'a> {
|
|||||||
&(f.as_ref().clone()).into(),
|
&(f.as_ref().clone()).into(),
|
||||||
rtxn,
|
rtxn,
|
||||||
index,
|
index,
|
||||||
filterable_fields,
|
field_ids_map,
|
||||||
|
filterable_attribute_rules,
|
||||||
universe,
|
universe,
|
||||||
)?;
|
)?;
|
||||||
match universe {
|
match universe {
|
||||||
@ -466,15 +481,14 @@ impl<'a> Filter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FilterCondition::In { fid, els } => {
|
FilterCondition::In { fid, els } => {
|
||||||
match matching_features(fid.value(), filterable_fields) {
|
match matching_features(fid.value(), filterable_attribute_rules) {
|
||||||
Some(features) if features.is_filterable() => {
|
Some((rule_index, features)) if features.is_filterable() => {
|
||||||
let field_ids_map = index.fields_ids_map(rtxn)?;
|
|
||||||
if let Some(fid) = field_ids_map.id(fid.value()) {
|
if let Some(fid) = field_ids_map.id(fid.value()) {
|
||||||
els.iter()
|
els.iter()
|
||||||
.map(|el| Condition::Equal(el.clone()))
|
.map(|el| Condition::Equal(el.clone()))
|
||||||
.map(|op| {
|
.map(|op| {
|
||||||
Self::evaluate_operator(
|
Self::evaluate_operator(
|
||||||
rtxn, index, fid, universe, &op, &features,
|
rtxn, index, fid, universe, &op, &features, rule_index,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.union()
|
.union()
|
||||||
@ -482,46 +496,50 @@ impl<'a> Filter<'a> {
|
|||||||
Ok(RoaringBitmap::new())
|
Ok(RoaringBitmap::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
||||||
let field_ids_map = index.fields_ids_map(rtxn)?;
|
|
||||||
Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
|
||||||
attribute: fid.value(),
|
attribute: fid.value(),
|
||||||
filterable_fields: filtered_matching_field_names(
|
filterable_fields: filtered_matching_field_names(
|
||||||
filterable_fields,
|
filterable_attribute_rules,
|
||||||
&field_ids_map,
|
&field_ids_map,
|
||||||
&|features| features.is_filterable(),
|
&|features| features.is_filterable(),
|
||||||
),
|
),
|
||||||
}))?
|
}))?,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FilterCondition::Condition { fid, op } => {
|
FilterCondition::Condition { fid, op } => {
|
||||||
match matching_features(fid.value(), filterable_fields) {
|
match matching_features(fid.value(), filterable_attribute_rules) {
|
||||||
Some(features) if features.is_filterable() => {
|
Some((rule_index, features)) if features.is_filterable() => {
|
||||||
let field_ids_map = index.fields_ids_map(rtxn)?;
|
|
||||||
if let Some(fid) = field_ids_map.id(fid.value()) {
|
if let Some(fid) = field_ids_map.id(fid.value()) {
|
||||||
Self::evaluate_operator(rtxn, index, fid, universe, op, &features)
|
Self::evaluate_operator(
|
||||||
|
rtxn, index, fid, universe, op, &features, rule_index,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(RoaringBitmap::new())
|
Ok(RoaringBitmap::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
||||||
let field_ids_map = index.fields_ids_map(rtxn)?;
|
|
||||||
Err(fid.as_external_error(FilterError::AttributeNotFilterable {
|
|
||||||
attribute: fid.value(),
|
attribute: fid.value(),
|
||||||
filterable_fields: filtered_matching_field_names(
|
filterable_fields: filtered_matching_field_names(
|
||||||
filterable_fields,
|
filterable_attribute_rules,
|
||||||
&field_ids_map,
|
&field_ids_map,
|
||||||
&|features| features.is_filterable(),
|
&|features| features.is_filterable(),
|
||||||
),
|
),
|
||||||
}))?
|
}))?,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FilterCondition::Or(subfilters) => subfilters
|
FilterCondition::Or(subfilters) => subfilters
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|f| Self::inner_evaluate(&f.into(), rtxn, index, filterable_fields, universe))
|
.map(|f| {
|
||||||
|
Self::inner_evaluate(
|
||||||
|
&f.into(),
|
||||||
|
rtxn,
|
||||||
|
index,
|
||||||
|
field_ids_map,
|
||||||
|
filterable_attribute_rules,
|
||||||
|
universe,
|
||||||
|
)
|
||||||
|
})
|
||||||
.union(),
|
.union(),
|
||||||
FilterCondition::And(subfilters) => {
|
FilterCondition::And(subfilters) => {
|
||||||
let mut subfilters_iter = subfilters.iter();
|
let mut subfilters_iter = subfilters.iter();
|
||||||
@ -530,7 +548,8 @@ impl<'a> Filter<'a> {
|
|||||||
&(first_subfilter.clone()).into(),
|
&(first_subfilter.clone()).into(),
|
||||||
rtxn,
|
rtxn,
|
||||||
index,
|
index,
|
||||||
filterable_fields,
|
field_ids_map,
|
||||||
|
filterable_attribute_rules,
|
||||||
universe,
|
universe,
|
||||||
)?;
|
)?;
|
||||||
for f in subfilters_iter {
|
for f in subfilters_iter {
|
||||||
@ -544,7 +563,8 @@ impl<'a> Filter<'a> {
|
|||||||
&(f.clone()).into(),
|
&(f.clone()).into(),
|
||||||
rtxn,
|
rtxn,
|
||||||
index,
|
index,
|
||||||
filterable_fields,
|
field_ids_map,
|
||||||
|
filterable_attribute_rules,
|
||||||
Some(&bitmap),
|
Some(&bitmap),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@ -582,11 +602,10 @@ impl<'a> Filter<'a> {
|
|||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
let field_ids_map = index.fields_ids_map(rtxn)?;
|
|
||||||
Err(point[0].as_external_error(FilterError::AttributeNotFilterable {
|
Err(point[0].as_external_error(FilterError::AttributeNotFilterable {
|
||||||
attribute: RESERVED_GEO_FIELD_NAME,
|
attribute: RESERVED_GEO_FIELD_NAME,
|
||||||
filterable_fields: filtered_matching_field_names(
|
filterable_fields: filtered_matching_field_names(
|
||||||
filterable_fields,
|
filterable_attribute_rules,
|
||||||
&field_ids_map,
|
&field_ids_map,
|
||||||
&|features| features.is_filterable(),
|
&|features| features.is_filterable(),
|
||||||
),
|
),
|
||||||
@ -649,7 +668,8 @@ impl<'a> Filter<'a> {
|
|||||||
let selected_lat = Filter { condition: condition_lat }.inner_evaluate(
|
let selected_lat = Filter { condition: condition_lat }.inner_evaluate(
|
||||||
rtxn,
|
rtxn,
|
||||||
index,
|
index,
|
||||||
filterable_fields,
|
field_ids_map,
|
||||||
|
filterable_attribute_rules,
|
||||||
universe,
|
universe,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -682,7 +702,8 @@ impl<'a> Filter<'a> {
|
|||||||
let left = Filter { condition: condition_left }.inner_evaluate(
|
let left = Filter { condition: condition_left }.inner_evaluate(
|
||||||
rtxn,
|
rtxn,
|
||||||
index,
|
index,
|
||||||
filterable_fields,
|
field_ids_map,
|
||||||
|
filterable_attribute_rules,
|
||||||
universe,
|
universe,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -696,7 +717,8 @@ impl<'a> Filter<'a> {
|
|||||||
let right = Filter { condition: condition_right }.inner_evaluate(
|
let right = Filter { condition: condition_right }.inner_evaluate(
|
||||||
rtxn,
|
rtxn,
|
||||||
index,
|
index,
|
||||||
filterable_fields,
|
field_ids_map,
|
||||||
|
filterable_attribute_rules,
|
||||||
universe,
|
universe,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -712,19 +734,19 @@ impl<'a> Filter<'a> {
|
|||||||
Filter { condition: condition_lng }.inner_evaluate(
|
Filter { condition: condition_lng }.inner_evaluate(
|
||||||
rtxn,
|
rtxn,
|
||||||
index,
|
index,
|
||||||
filterable_fields,
|
field_ids_map,
|
||||||
|
filterable_attribute_rules,
|
||||||
universe,
|
universe,
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(selected_lat & selected_lng)
|
Ok(selected_lat & selected_lng)
|
||||||
} else {
|
} else {
|
||||||
let field_ids_map = index.fields_ids_map(rtxn)?;
|
|
||||||
Err(top_right_point[0].as_external_error(
|
Err(top_right_point[0].as_external_error(
|
||||||
FilterError::AttributeNotFilterable {
|
FilterError::AttributeNotFilterable {
|
||||||
attribute: RESERVED_GEO_FIELD_NAME,
|
attribute: RESERVED_GEO_FIELD_NAME,
|
||||||
filterable_fields: filtered_matching_field_names(
|
filterable_fields: filtered_matching_field_names(
|
||||||
filterable_fields,
|
filterable_attribute_rules,
|
||||||
&field_ids_map,
|
&field_ids_map,
|
||||||
&|features| features.is_filterable(),
|
&|features| features.is_filterable(),
|
||||||
),
|
),
|
||||||
@ -742,6 +764,7 @@ fn generate_filter_error(
|
|||||||
field_id: FieldId,
|
field_id: FieldId,
|
||||||
operator: &Condition<'_>,
|
operator: &Condition<'_>,
|
||||||
features: &FilterableAttributesFeatures,
|
features: &FilterableAttributesFeatures,
|
||||||
|
rule_index: usize,
|
||||||
) -> Error {
|
) -> Error {
|
||||||
match index.fields_ids_map(rtxn) {
|
match index.fields_ids_map(rtxn) {
|
||||||
Ok(fields_ids_map) => {
|
Ok(fields_ids_map) => {
|
||||||
@ -750,6 +773,7 @@ fn generate_filter_error(
|
|||||||
field: field.to_string(),
|
field: field.to_string(),
|
||||||
allowed_operators: features.allowed_filter_operators(),
|
allowed_operators: features.allowed_filter_operators(),
|
||||||
operator: operator.operator().to_string(),
|
operator: operator.operator().to_string(),
|
||||||
|
rule_index,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(e) => e.into(),
|
Err(e) => e.into(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user