mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-12-04 18:45:46 +01:00
Start with a filterBoosting ranking rule when boostingFilter is present
This commit is contained in:
parent
f2041fd78c
commit
b43edabbcd
@ -332,6 +332,7 @@ impl ErrorCode for milli::Error {
|
|||||||
UserError::MaxDatabaseSizeReached => Code::DatabaseSizeLimitReached,
|
UserError::MaxDatabaseSizeReached => Code::DatabaseSizeLimitReached,
|
||||||
UserError::AttributeLimitReached => Code::MaxFieldsLimitExceeded,
|
UserError::AttributeLimitReached => Code::MaxFieldsLimitExceeded,
|
||||||
UserError::InvalidFilter(_) => Code::InvalidSearchFilter,
|
UserError::InvalidFilter(_) => Code::InvalidSearchFilter,
|
||||||
|
UserError::InvalidBoostingFilter(_) => Code::InvalidSearchBoostingFilter,
|
||||||
UserError::InvalidFilterExpression(..) => Code::InvalidSearchFilter,
|
UserError::InvalidFilterExpression(..) => Code::InvalidSearchFilter,
|
||||||
UserError::MissingDocumentId { .. } => Code::MissingDocumentId,
|
UserError::MissingDocumentId { .. } => Code::MissingDocumentId,
|
||||||
UserError::InvalidDocumentId { .. } | UserError::TooManyDocumentIds { .. } => {
|
UserError::InvalidDocumentId { .. } | UserError::TooManyDocumentIds { .. } => {
|
||||||
|
@ -650,7 +650,7 @@ impl From<RankingRule> for RankingRuleView {
|
|||||||
fn from(value: RankingRule) -> Self {
|
fn from(value: RankingRule) -> Self {
|
||||||
match value {
|
match value {
|
||||||
RankingRule::Words => RankingRuleView::Words,
|
RankingRule::Words => RankingRuleView::Words,
|
||||||
RankingRule::Boost(filter) => RankingRuleView::Boost(filter),
|
RankingRule::FilterBoosting(filter) => RankingRuleView::Boost(filter),
|
||||||
RankingRule::Typo => RankingRuleView::Typo,
|
RankingRule::Typo => RankingRuleView::Typo,
|
||||||
RankingRule::Proximity => RankingRuleView::Proximity,
|
RankingRule::Proximity => RankingRuleView::Proximity,
|
||||||
RankingRule::Attribute => RankingRuleView::Attribute,
|
RankingRule::Attribute => RankingRuleView::Attribute,
|
||||||
@ -665,7 +665,7 @@ impl From<RankingRuleView> for RankingRule {
|
|||||||
fn from(value: RankingRuleView) -> Self {
|
fn from(value: RankingRuleView) -> Self {
|
||||||
match value {
|
match value {
|
||||||
RankingRuleView::Words => RankingRule::Words,
|
RankingRuleView::Words => RankingRule::Words,
|
||||||
RankingRuleView::Boost(filter) => RankingRule::Boost(filter),
|
RankingRuleView::Boost(filter) => RankingRule::FilterBoosting(filter),
|
||||||
RankingRuleView::Typo => RankingRule::Typo,
|
RankingRuleView::Typo => RankingRule::Typo,
|
||||||
RankingRuleView::Proximity => RankingRule::Proximity,
|
RankingRuleView::Proximity => RankingRule::Proximity,
|
||||||
RankingRuleView::Attribute => RankingRule::Attribute,
|
RankingRuleView::Attribute => RankingRule::Attribute,
|
||||||
|
@ -58,6 +58,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
false,
|
false,
|
||||||
&None,
|
&None,
|
||||||
&None,
|
&None,
|
||||||
|
&None,
|
||||||
GeoSortStrategy::default(),
|
GeoSortStrategy::default(),
|
||||||
0,
|
0,
|
||||||
20,
|
20,
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
//! This module provides the `Boost` type and defines all the errors related to this type.
|
|
||||||
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::RankingRuleError;
|
|
||||||
|
|
||||||
/// This error type is never supposed to be shown to the end user.
|
|
||||||
/// You must always cast it to a sort error or a criterion error.
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum BoostError {
|
|
||||||
#[error("Invalid syntax for the boost parameter: expected expression starting with `boost:`, found `{name}`.")]
|
|
||||||
InvalidSyntax { name: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BoostError> for RankingRuleError {
|
|
||||||
fn from(error: BoostError) -> Self {
|
|
||||||
match error {
|
|
||||||
BoostError::InvalidSyntax { name } => RankingRuleError::InvalidName { name },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
|
||||||
pub struct Boost(pub String);
|
|
||||||
|
|
||||||
impl FromStr for Boost {
|
|
||||||
type Err = BoostError;
|
|
||||||
|
|
||||||
fn from_str(text: &str) -> Result<Boost, Self::Err> {
|
|
||||||
match text.split_once(':') {
|
|
||||||
Some(("boost", right)) => Ok(Boost(right.to_string())), // TODO check filter validity
|
|
||||||
_ => Err(BoostError::InvalidSyntax { name: text.to_string() }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -116,6 +116,8 @@ only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and undersco
|
|||||||
InvalidVectorsType { document_id: Value, value: Value },
|
InvalidVectorsType { document_id: Value, value: Value },
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InvalidFilter(String),
|
InvalidFilter(String),
|
||||||
|
#[error("{0}")]
|
||||||
|
InvalidBoostingFilter(String),
|
||||||
#[error("Invalid type for filter subexpression: expected: {}, found: {1}.", .0.join(", "))]
|
#[error("Invalid type for filter subexpression: expected: {}, found: {1}.", .0.join(", "))]
|
||||||
InvalidFilterExpression(&'static [&'static str], Value),
|
InvalidFilterExpression(&'static [&'static str], Value),
|
||||||
#[error("Attribute `{}` is not sortable. {}",
|
#[error("Attribute `{}` is not sortable. {}",
|
||||||
|
@ -9,7 +9,6 @@ pub static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
|||||||
pub mod documents;
|
pub mod documents;
|
||||||
|
|
||||||
mod asc_desc;
|
mod asc_desc;
|
||||||
mod boost;
|
|
||||||
pub mod distance;
|
pub mod distance;
|
||||||
mod error;
|
mod error;
|
||||||
mod external_documents_ids;
|
mod external_documents_ids;
|
||||||
|
@ -4,8 +4,7 @@ use std::str::FromStr;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::boost::{Boost, BoostError};
|
use crate::{AscDesc, Member};
|
||||||
use crate::{AscDesc, AscDescError, Member};
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum RankingRuleError {
|
pub enum RankingRuleError {
|
||||||
@ -27,11 +26,11 @@ pub enum RankingRuleError {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
pub enum RankingRule {
|
pub enum RankingRule {
|
||||||
|
/// Sorted by documents matching the given filter and then documents not matching it.
|
||||||
|
FilterBoosting(String),
|
||||||
/// Sorted by decreasing number of matched query terms.
|
/// Sorted by decreasing number of matched query terms.
|
||||||
/// Query words at the front of an attribute is considered better than if it was at the back.
|
/// Query words at the front of an attribute is considered better than if it was at the back.
|
||||||
Words,
|
Words,
|
||||||
/// Sorted by documents matching the given filter and then documents not matching it.
|
|
||||||
Boost(String),
|
|
||||||
/// Sorted by increasing number of typos.
|
/// Sorted by increasing number of typos.
|
||||||
Typo,
|
Typo,
|
||||||
/// Sorted by increasing distance between matched query terms.
|
/// Sorted by increasing distance between matched query terms.
|
||||||
@ -71,8 +70,8 @@ impl FromStr for RankingRule {
|
|||||||
"attribute" => Ok(RankingRule::Attribute),
|
"attribute" => Ok(RankingRule::Attribute),
|
||||||
"sort" => Ok(RankingRule::Sort),
|
"sort" => Ok(RankingRule::Sort),
|
||||||
"exactness" => Ok(RankingRule::Exactness),
|
"exactness" => Ok(RankingRule::Exactness),
|
||||||
text => match (AscDesc::from_str(text), Boost::from_str(text)) {
|
text => match AscDesc::from_str(text) {
|
||||||
(Ok(asc_desc), _) => match asc_desc {
|
Ok(asc_desc) => match asc_desc {
|
||||||
AscDesc::Asc(Member::Field(field)) => Ok(RankingRule::Asc(field)),
|
AscDesc::Asc(Member::Field(field)) => Ok(RankingRule::Asc(field)),
|
||||||
AscDesc::Desc(Member::Field(field)) => Ok(RankingRule::Desc(field)),
|
AscDesc::Desc(Member::Field(field)) => Ok(RankingRule::Desc(field)),
|
||||||
AscDesc::Asc(Member::Geo(_)) | AscDesc::Desc(Member::Geo(_)) => {
|
AscDesc::Asc(Member::Geo(_)) | AscDesc::Desc(Member::Geo(_)) => {
|
||||||
@ -81,15 +80,7 @@ impl FromStr for RankingRule {
|
|||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(_, Ok(Boost(filter))) => Ok(RankingRule::Boost(filter)),
|
Err(err) => Err(err.into()),
|
||||||
(
|
|
||||||
Err(AscDescError::InvalidSyntax { name: asc_desc_name }),
|
|
||||||
Err(BoostError::InvalidSyntax { name: boost_name }),
|
|
||||||
) => Err(RankingRuleError::InvalidName {
|
|
||||||
// TODO improve the error message quality
|
|
||||||
name: format!("{asc_desc_name} {boost_name}"),
|
|
||||||
}),
|
|
||||||
(Err(asc_desc_error), _) => Err(asc_desc_error.into()),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +103,7 @@ impl fmt::Display for RankingRule {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
Words => f.write_str("words"),
|
Words => f.write_str("words"),
|
||||||
Boost(filter) => write!(f, "boost:{filter}"),
|
FilterBoosting(_) => write!(f, "filterBoosting"),
|
||||||
Typo => f.write_str("typo"),
|
Typo => f.write_str("typo"),
|
||||||
Proximity => f.write_str("proximity"),
|
Proximity => f.write_str("proximity"),
|
||||||
Attribute => f.write_str("attribute"),
|
Attribute => f.write_str("attribute"),
|
||||||
|
@ -5,7 +5,7 @@ use crate::distance_between_two_points;
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ScoreDetails {
|
pub enum ScoreDetails {
|
||||||
Words(Words),
|
Words(Words),
|
||||||
Boost(Boost),
|
FilterBoosting(FilterBoosting),
|
||||||
Typo(Typo),
|
Typo(Typo),
|
||||||
Proximity(Rank),
|
Proximity(Rank),
|
||||||
Fid(Rank),
|
Fid(Rank),
|
||||||
@ -24,7 +24,7 @@ impl ScoreDetails {
|
|||||||
pub fn rank(&self) -> Option<Rank> {
|
pub fn rank(&self) -> Option<Rank> {
|
||||||
match self {
|
match self {
|
||||||
ScoreDetails::Words(details) => Some(details.rank()),
|
ScoreDetails::Words(details) => Some(details.rank()),
|
||||||
ScoreDetails::Boost(_) => None,
|
ScoreDetails::FilterBoosting(_) => None,
|
||||||
ScoreDetails::Typo(details) => Some(details.rank()),
|
ScoreDetails::Typo(details) => Some(details.rank()),
|
||||||
ScoreDetails::Proximity(details) => Some(*details),
|
ScoreDetails::Proximity(details) => Some(*details),
|
||||||
ScoreDetails::Fid(details) => Some(*details),
|
ScoreDetails::Fid(details) => Some(*details),
|
||||||
@ -62,12 +62,9 @@ impl ScoreDetails {
|
|||||||
details_map.insert("words".into(), words_details);
|
details_map.insert("words".into(), words_details);
|
||||||
order += 1;
|
order += 1;
|
||||||
}
|
}
|
||||||
ScoreDetails::Boost(Boost { filter, matching }) => {
|
ScoreDetails::FilterBoosting(FilterBoosting { matching }) => {
|
||||||
let sort = format!("boost:{}", filter);
|
let sort_details = serde_json::json!({ "matching": matching });
|
||||||
let sort_details = serde_json::json!({
|
details_map.insert("filterBoosting".into(), sort_details);
|
||||||
"value": matching,
|
|
||||||
});
|
|
||||||
details_map.insert(sort, sort_details);
|
|
||||||
order += 1;
|
order += 1;
|
||||||
}
|
}
|
||||||
ScoreDetails::Typo(typo) => {
|
ScoreDetails::Typo(typo) => {
|
||||||
@ -232,8 +229,7 @@ impl Words {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Boost {
|
pub struct FilterBoosting {
|
||||||
pub filter: String,
|
|
||||||
pub matching: bool,
|
pub matching: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ pub struct Search<'a> {
|
|||||||
vector: Option<Vec<f32>>,
|
vector: Option<Vec<f32>>,
|
||||||
// this should be linked to the String in the query
|
// this should be linked to the String in the query
|
||||||
filter: Option<Filter<'a>>,
|
filter: Option<Filter<'a>>,
|
||||||
|
boosting_filter: Option<Filter<'a>>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
sort_criteria: Option<Vec<AscDesc>>,
|
sort_criteria: Option<Vec<AscDesc>>,
|
||||||
@ -57,6 +58,7 @@ impl<'a> Search<'a> {
|
|||||||
query: None,
|
query: None,
|
||||||
vector: None,
|
vector: None,
|
||||||
filter: None,
|
filter: None,
|
||||||
|
boosting_filter: None,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
sort_criteria: None,
|
sort_criteria: None,
|
||||||
@ -121,6 +123,11 @@ impl<'a> Search<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn boosting_filter(&mut self, condition: Filter<'a>) -> &mut Search<'a> {
|
||||||
|
self.boosting_filter = Some(condition);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn geo_sort_strategy(&mut self, strategy: new::GeoSortStrategy) -> &mut Search<'a> {
|
pub fn geo_sort_strategy(&mut self, strategy: new::GeoSortStrategy) -> &mut Search<'a> {
|
||||||
self.geo_strategy = strategy;
|
self.geo_strategy = strategy;
|
||||||
@ -150,6 +157,7 @@ impl<'a> Search<'a> {
|
|||||||
self.scoring_strategy,
|
self.scoring_strategy,
|
||||||
self.exhaustive_number_hits,
|
self.exhaustive_number_hits,
|
||||||
&self.filter,
|
&self.filter,
|
||||||
|
&self.boosting_filter,
|
||||||
&self.sort_criteria,
|
&self.sort_criteria,
|
||||||
self.geo_strategy,
|
self.geo_strategy,
|
||||||
self.offset,
|
self.offset,
|
||||||
@ -175,6 +183,7 @@ impl fmt::Debug for Search<'_> {
|
|||||||
query,
|
query,
|
||||||
vector: _,
|
vector: _,
|
||||||
filter,
|
filter,
|
||||||
|
boosting_filter,
|
||||||
offset,
|
offset,
|
||||||
limit,
|
limit,
|
||||||
sort_criteria,
|
sort_criteria,
|
||||||
@ -191,6 +200,7 @@ impl fmt::Debug for Search<'_> {
|
|||||||
.field("query", query)
|
.field("query", query)
|
||||||
.field("vector", &"[...]")
|
.field("vector", &"[...]")
|
||||||
.field("filter", filter)
|
.field("filter", filter)
|
||||||
|
.field("boosting_filter", boosting_filter)
|
||||||
.field("offset", offset)
|
.field("offset", offset)
|
||||||
.field("limit", limit)
|
.field("limit", limit)
|
||||||
.field("sort_criteria", sort_criteria)
|
.field("sort_criteria", sort_criteria)
|
||||||
|
@ -5,29 +5,26 @@ use super::{RankingRule, RankingRuleOutput, RankingRuleQueryTrait, SearchContext
|
|||||||
use crate::score_details::{self, ScoreDetails};
|
use crate::score_details::{self, ScoreDetails};
|
||||||
use crate::{Filter, Result};
|
use crate::{Filter, Result};
|
||||||
|
|
||||||
pub struct Boost<Query> {
|
pub struct FilterBoosting<'f, Query> {
|
||||||
original_expression: String,
|
filter: Filter<'f>,
|
||||||
original_query: Option<Query>,
|
original_query: Option<Query>,
|
||||||
matching: Option<RankingRuleOutput<Query>>,
|
matching: Option<RankingRuleOutput<Query>>,
|
||||||
non_matching: Option<RankingRuleOutput<Query>>,
|
non_matching: Option<RankingRuleOutput<Query>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Query> Boost<Query> {
|
impl<'f, Query> FilterBoosting<'f, Query> {
|
||||||
pub fn new(expression: String) -> Result<Self> {
|
pub fn new(filter: Filter<'f>) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self { filter, original_query: None, matching: None, non_matching: None })
|
||||||
original_expression: expression,
|
|
||||||
original_query: None,
|
|
||||||
matching: None,
|
|
||||||
non_matching: None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, Query: RankingRuleQueryTrait> RankingRule<'ctx, Query> for Boost<Query> {
|
impl<'ctx, 'f, Query: RankingRuleQueryTrait> RankingRule<'ctx, Query>
|
||||||
|
for FilterBoosting<'f, Query>
|
||||||
|
{
|
||||||
fn id(&self) -> String {
|
fn id(&self) -> String {
|
||||||
// TODO improve this
|
// TODO improve this
|
||||||
let Self { original_expression, .. } = self;
|
let Self { filter: original_expression, .. } = self;
|
||||||
format!("boost:{original_expression}")
|
format!("boost:{original_expression:?}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_iteration(
|
fn start_iteration(
|
||||||
@ -37,9 +34,9 @@ impl<'ctx, Query: RankingRuleQueryTrait> RankingRule<'ctx, Query> for Boost<Quer
|
|||||||
parent_candidates: &RoaringBitmap,
|
parent_candidates: &RoaringBitmap,
|
||||||
parent_query: &Query,
|
parent_query: &Query,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let universe_matching = match Filter::from_str(&self.original_expression)? {
|
let universe_matching = match self.filter.evaluate(ctx.txn, ctx.index) {
|
||||||
Some(filter) => filter.evaluate(ctx.txn, ctx.index)?,
|
Ok(documents) => documents,
|
||||||
None => RoaringBitmap::default(),
|
Err(e) => return Err(e), // TODO manage the invalid_search_boosting_filter
|
||||||
};
|
};
|
||||||
let matching = parent_candidates & universe_matching;
|
let matching = parent_candidates & universe_matching;
|
||||||
let non_matching = parent_candidates - &matching;
|
let non_matching = parent_candidates - &matching;
|
||||||
@ -49,19 +46,13 @@ impl<'ctx, Query: RankingRuleQueryTrait> RankingRule<'ctx, Query> for Boost<Quer
|
|||||||
self.matching = Some(RankingRuleOutput {
|
self.matching = Some(RankingRuleOutput {
|
||||||
query: parent_query.clone(),
|
query: parent_query.clone(),
|
||||||
candidates: matching,
|
candidates: matching,
|
||||||
score: ScoreDetails::Boost(score_details::Boost {
|
score: ScoreDetails::FilterBoosting(score_details::FilterBoosting { matching: true }),
|
||||||
filter: self.original_expression.clone(),
|
|
||||||
matching: true,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self.non_matching = Some(RankingRuleOutput {
|
self.non_matching = Some(RankingRuleOutput {
|
||||||
query: parent_query.clone(),
|
query: parent_query.clone(),
|
||||||
candidates: non_matching,
|
candidates: non_matching,
|
||||||
score: ScoreDetails::Boost(score_details::Boost {
|
score: ScoreDetails::FilterBoosting(score_details::FilterBoosting { matching: false }),
|
||||||
filter: self.original_expression.clone(),
|
|
||||||
matching: false,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
@ -512,6 +512,7 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&None,
|
&None,
|
||||||
&None,
|
&None,
|
||||||
|
&None,
|
||||||
crate::search::new::GeoSortStrategy::default(),
|
crate::search::new::GeoSortStrategy::default(),
|
||||||
0,
|
0,
|
||||||
100,
|
100,
|
||||||
|
@ -14,8 +14,8 @@ mod ranking_rules;
|
|||||||
mod resolve_query_graph;
|
mod resolve_query_graph;
|
||||||
mod small_bitmap;
|
mod small_bitmap;
|
||||||
|
|
||||||
mod boost;
|
|
||||||
mod exact_attribute;
|
mod exact_attribute;
|
||||||
|
mod filter_boosting;
|
||||||
mod sort;
|
mod sort;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -23,11 +23,11 @@ mod tests;
|
|||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use boost::Boost;
|
|
||||||
use bucket_sort::{bucket_sort, BucketSortOutput};
|
use bucket_sort::{bucket_sort, BucketSortOutput};
|
||||||
use charabia::TokenizerBuilder;
|
use charabia::TokenizerBuilder;
|
||||||
use db_cache::DatabaseCache;
|
use db_cache::DatabaseCache;
|
||||||
use exact_attribute::ExactAttribute;
|
use exact_attribute::ExactAttribute;
|
||||||
|
use filter_boosting::FilterBoosting;
|
||||||
use graph_based_ranking_rule::{Exactness, Fid, Position, Proximity, Typo};
|
use graph_based_ranking_rule::{Exactness, Fid, Position, Proximity, Typo};
|
||||||
use heed::RoTxn;
|
use heed::RoTxn;
|
||||||
use instant_distance::Search;
|
use instant_distance::Search;
|
||||||
@ -192,25 +192,29 @@ fn resolve_universe(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the list of initialised ranking rules to be used for a placeholder search.
|
/// Return the list of initialised ranking rules to be used for a placeholder search.
|
||||||
fn get_ranking_rules_for_placeholder_search<'ctx>(
|
fn get_ranking_rules_for_placeholder_search<'ctx, 'f: 'ctx>(
|
||||||
ctx: &SearchContext<'ctx>,
|
ctx: &SearchContext<'ctx>,
|
||||||
sort_criteria: &Option<Vec<AscDesc>>,
|
sort_criteria: &Option<Vec<AscDesc>>,
|
||||||
geo_strategy: geo_sort::Strategy,
|
geo_strategy: geo_sort::Strategy,
|
||||||
|
boosting_filter: &Option<Filter<'f>>,
|
||||||
) -> Result<Vec<BoxRankingRule<'ctx, PlaceholderQuery>>> {
|
) -> Result<Vec<BoxRankingRule<'ctx, PlaceholderQuery>>> {
|
||||||
let mut sort = false;
|
let mut sort = false;
|
||||||
let mut sorted_fields = HashSet::new();
|
let mut sorted_fields = HashSet::new();
|
||||||
let mut geo_sorted = false;
|
let mut geo_sorted = false;
|
||||||
let mut ranking_rules: Vec<BoxRankingRule<PlaceholderQuery>> = vec![];
|
let mut ranking_rules: Vec<BoxRankingRule<_>> = match boosting_filter {
|
||||||
|
Some(filter) => vec![Box::new(FilterBoosting::new(filter.clone())?)],
|
||||||
|
None => Vec::new(),
|
||||||
|
};
|
||||||
let settings_ranking_rules = ctx.index.criteria(ctx.txn)?;
|
let settings_ranking_rules = ctx.index.criteria(ctx.txn)?;
|
||||||
for rr in settings_ranking_rules {
|
for rr in settings_ranking_rules {
|
||||||
match rr {
|
match rr {
|
||||||
// These rules need a query to have an effect; ignore them in placeholder search
|
// These rules need a query to have an effect; ignore them in placeholder search
|
||||||
crate::RankingRule::Words
|
crate::RankingRule::FilterBoosting(_)
|
||||||
|
| crate::RankingRule::Words
|
||||||
| crate::RankingRule::Typo
|
| crate::RankingRule::Typo
|
||||||
| crate::RankingRule::Attribute
|
| crate::RankingRule::Attribute
|
||||||
| crate::RankingRule::Proximity
|
| crate::RankingRule::Proximity
|
||||||
| crate::RankingRule::Exactness => continue,
|
| crate::RankingRule::Exactness => continue,
|
||||||
crate::RankingRule::Boost(filter) => ranking_rules.push(Box::new(Boost::new(filter)?)),
|
|
||||||
crate::RankingRule::Sort => {
|
crate::RankingRule::Sort => {
|
||||||
if sort {
|
if sort {
|
||||||
continue;
|
continue;
|
||||||
@ -245,11 +249,12 @@ fn get_ranking_rules_for_placeholder_search<'ctx>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the list of initialised ranking rules to be used for a query graph search.
|
/// Return the list of initialised ranking rules to be used for a query graph search.
|
||||||
fn get_ranking_rules_for_query_graph_search<'ctx>(
|
fn get_ranking_rules_for_query_graph_search<'ctx, 'f: 'ctx>(
|
||||||
ctx: &SearchContext<'ctx>,
|
ctx: &SearchContext<'ctx>,
|
||||||
sort_criteria: &Option<Vec<AscDesc>>,
|
sort_criteria: &Option<Vec<AscDesc>>,
|
||||||
geo_strategy: geo_sort::Strategy,
|
geo_strategy: geo_sort::Strategy,
|
||||||
terms_matching_strategy: TermsMatchingStrategy,
|
terms_matching_strategy: TermsMatchingStrategy,
|
||||||
|
boosting_filter: &Option<Filter<'f>>,
|
||||||
) -> Result<Vec<BoxRankingRule<'ctx, QueryGraph>>> {
|
) -> Result<Vec<BoxRankingRule<'ctx, QueryGraph>>> {
|
||||||
// query graph search
|
// query graph search
|
||||||
let mut words = false;
|
let mut words = false;
|
||||||
@ -266,7 +271,10 @@ fn get_ranking_rules_for_query_graph_search<'ctx>(
|
|||||||
words = true;
|
words = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ranking_rules: Vec<BoxRankingRule<QueryGraph>> = vec![];
|
let mut ranking_rules: Vec<BoxRankingRule<QueryGraph>> = match boosting_filter {
|
||||||
|
Some(filter) => vec![Box::new(FilterBoosting::new(filter.clone())?)],
|
||||||
|
None => Vec::new(),
|
||||||
|
};
|
||||||
let settings_ranking_rules = ctx.index.criteria(ctx.txn)?;
|
let settings_ranking_rules = ctx.index.criteria(ctx.txn)?;
|
||||||
for rr in settings_ranking_rules {
|
for rr in settings_ranking_rules {
|
||||||
// Add Words before any of: typo, proximity, attribute
|
// Add Words before any of: typo, proximity, attribute
|
||||||
@ -290,8 +298,10 @@ fn get_ranking_rules_for_query_graph_search<'ctx>(
|
|||||||
ranking_rules.push(Box::new(Words::new(terms_matching_strategy)));
|
ranking_rules.push(Box::new(Words::new(terms_matching_strategy)));
|
||||||
words = true;
|
words = true;
|
||||||
}
|
}
|
||||||
crate::RankingRule::Boost(filter) => {
|
crate::RankingRule::FilterBoosting(_) => {
|
||||||
ranking_rules.push(Box::new(Boost::new(filter)?));
|
// that is not possible to define the filterBoosting ranking rule by hand,
|
||||||
|
// or by using the seetings. It is always inserted by the engine itself.
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
crate::RankingRule::Typo => {
|
crate::RankingRule::Typo => {
|
||||||
if typo {
|
if typo {
|
||||||
@ -413,14 +423,15 @@ fn resolve_sort_criteria<'ctx, Query: RankingRuleQueryTrait>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn execute_search(
|
pub fn execute_search<'ctx, 'f: 'ctx>(
|
||||||
ctx: &mut SearchContext,
|
ctx: &mut SearchContext<'ctx>,
|
||||||
query: &Option<String>,
|
query: &Option<String>,
|
||||||
vector: &Option<Vec<f32>>,
|
vector: &Option<Vec<f32>>,
|
||||||
terms_matching_strategy: TermsMatchingStrategy,
|
terms_matching_strategy: TermsMatchingStrategy,
|
||||||
scoring_strategy: ScoringStrategy,
|
scoring_strategy: ScoringStrategy,
|
||||||
exhaustive_number_hits: bool,
|
exhaustive_number_hits: bool,
|
||||||
filters: &Option<Filter>,
|
filter: &Option<Filter>,
|
||||||
|
boosting_filter: &Option<Filter<'f>>,
|
||||||
sort_criteria: &Option<Vec<AscDesc>>,
|
sort_criteria: &Option<Vec<AscDesc>>,
|
||||||
geo_strategy: geo_sort::Strategy,
|
geo_strategy: geo_sort::Strategy,
|
||||||
from: usize,
|
from: usize,
|
||||||
@ -429,8 +440,8 @@ pub fn execute_search(
|
|||||||
placeholder_search_logger: &mut dyn SearchLogger<PlaceholderQuery>,
|
placeholder_search_logger: &mut dyn SearchLogger<PlaceholderQuery>,
|
||||||
query_graph_logger: &mut dyn SearchLogger<QueryGraph>,
|
query_graph_logger: &mut dyn SearchLogger<QueryGraph>,
|
||||||
) -> Result<PartialSearchResult> {
|
) -> Result<PartialSearchResult> {
|
||||||
let mut universe = if let Some(filters) = filters {
|
let mut universe = if let Some(filter) = filter {
|
||||||
filters.evaluate(ctx.txn, ctx.index)?
|
filter.evaluate(ctx.txn, ctx.index)?
|
||||||
} else {
|
} else {
|
||||||
ctx.index.documents_ids(ctx.txn)?
|
ctx.index.documents_ids(ctx.txn)?
|
||||||
};
|
};
|
||||||
@ -523,6 +534,7 @@ pub fn execute_search(
|
|||||||
sort_criteria,
|
sort_criteria,
|
||||||
geo_strategy,
|
geo_strategy,
|
||||||
terms_matching_strategy,
|
terms_matching_strategy,
|
||||||
|
boosting_filter,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
universe =
|
universe =
|
||||||
@ -539,8 +551,13 @@ pub fn execute_search(
|
|||||||
query_graph_logger,
|
query_graph_logger,
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
let ranking_rules =
|
let ranking_rules = get_ranking_rules_for_placeholder_search(
|
||||||
get_ranking_rules_for_placeholder_search(ctx, sort_criteria, geo_strategy)?;
|
ctx,
|
||||||
|
sort_criteria,
|
||||||
|
geo_strategy,
|
||||||
|
boosting_filter,
|
||||||
|
)?;
|
||||||
|
|
||||||
bucket_sort(
|
bucket_sort(
|
||||||
ctx,
|
ctx,
|
||||||
ranking_rules,
|
ranking_rules,
|
||||||
|
@ -147,7 +147,7 @@ pub fn expected_order(
|
|||||||
new_groups
|
new_groups
|
||||||
.extend(group.linear_group_by_key(|d| d.asc_desc_rank).map(Vec::from));
|
.extend(group.linear_group_by_key(|d| d.asc_desc_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Boost(filter) => {
|
RankingRule::FilterBoosting(filter) => {
|
||||||
// move the matching documents first, then the ones that don't match
|
// move the matching documents first, then the ones that don't match
|
||||||
group.sort_by_key(|d| if execute_filter(filter, d).is_some() { 0 } else { 1 });
|
group.sort_by_key(|d| if execute_filter(filter, d).is_some() { 0 } else { 1 });
|
||||||
new_groups.extend(
|
new_groups.extend(
|
||||||
|
Loading…
Reference in New Issue
Block a user