mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-07-20 05:20:36 +00:00
Compare commits
5 Commits
v1.13.0
...
document-b
Author | SHA1 | Date | |
---|---|---|---|
b43edabbcd | |||
f2041fd78c | |||
2495058a6e | |||
4cfb48fbb6 | |||
67dc0268c5 |
@ -12,7 +12,7 @@ use milli::heed::EnvOpenOptions;
|
|||||||
use milli::update::{
|
use milli::update::{
|
||||||
IndexDocuments, IndexDocumentsConfig, IndexDocumentsMethod, IndexerConfig, Settings,
|
IndexDocuments, IndexDocumentsConfig, IndexDocumentsMethod, IndexerConfig, Settings,
|
||||||
};
|
};
|
||||||
use milli::{Criterion, Filter, Index, Object, TermsMatchingStrategy};
|
use milli::{Filter, Index, Object, RankingRule, TermsMatchingStrategy};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
pub struct Conf<'a> {
|
pub struct Conf<'a> {
|
||||||
@ -78,11 +78,11 @@ pub fn base_setup(conf: &Conf) -> Index {
|
|||||||
|
|
||||||
if let Some(criterion) = conf.criterion {
|
if let Some(criterion) = conf.criterion {
|
||||||
builder.reset_filterable_fields();
|
builder.reset_filterable_fields();
|
||||||
builder.reset_criteria();
|
builder.reset_ranking_rules();
|
||||||
builder.reset_stop_words();
|
builder.reset_stop_words();
|
||||||
|
|
||||||
let criterion = criterion.iter().map(|s| Criterion::from_str(s).unwrap()).collect();
|
let criterion = criterion.iter().map(|s| RankingRule::from_str(s).unwrap()).collect();
|
||||||
builder.set_criteria(criterion);
|
builder.set_ranking_rules(criterion);
|
||||||
}
|
}
|
||||||
|
|
||||||
(conf.configure)(&mut builder);
|
(conf.configure)(&mut builder);
|
||||||
|
@ -235,6 +235,7 @@ InvalidSearchCropMarker , InvalidRequest , BAD_REQUEST ;
|
|||||||
InvalidSearchFacets , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchFacets , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidFacetSearchFacetName , InvalidRequest , BAD_REQUEST ;
|
InvalidFacetSearchFacetName , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchFilter , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchFilter , InvalidRequest , BAD_REQUEST ;
|
||||||
|
InvalidSearchBoostingFilter , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchHighlightPostTag , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchHighlightPostTag , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchHighlightPreTag , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchHighlightPreTag , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchHitsPerPage , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchHitsPerPage , InvalidRequest , BAD_REQUEST ;
|
||||||
@ -331,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 { .. } => {
|
||||||
|
@ -9,7 +9,7 @@ use std::str::FromStr;
|
|||||||
use deserr::{DeserializeError, Deserr, ErrorKind, MergeWithError, ValuePointerRef};
|
use deserr::{DeserializeError, Deserr, ErrorKind, MergeWithError, ValuePointerRef};
|
||||||
use fst::IntoStreamer;
|
use fst::IntoStreamer;
|
||||||
use milli::update::Setting;
|
use milli::update::Setting;
|
||||||
use milli::{Criterion, CriterionError, Index, DEFAULT_VALUES_PER_FACET};
|
use milli::{Index, RankingRule, RankingRuleError, DEFAULT_VALUES_PER_FACET};
|
||||||
use serde::{Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
|
||||||
use crate::deserr::DeserrJsonError;
|
use crate::deserr::DeserrJsonError;
|
||||||
@ -117,10 +117,10 @@ pub struct PaginationSettings {
|
|||||||
pub max_total_hits: Setting<usize>,
|
pub max_total_hits: Setting<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MergeWithError<milli::CriterionError> for DeserrJsonError<InvalidSettingsRankingRules> {
|
impl MergeWithError<milli::RankingRuleError> for DeserrJsonError<InvalidSettingsRankingRules> {
|
||||||
fn merge(
|
fn merge(
|
||||||
_self_: Option<Self>,
|
_self_: Option<Self>,
|
||||||
other: milli::CriterionError,
|
other: milli::RankingRuleError,
|
||||||
merge_location: ValuePointerRef,
|
merge_location: ValuePointerRef,
|
||||||
) -> ControlFlow<Self, Self> {
|
) -> ControlFlow<Self, Self> {
|
||||||
Self::error::<Infallible>(
|
Self::error::<Infallible>(
|
||||||
@ -344,9 +344,9 @@ pub fn apply_settings_to_builder(
|
|||||||
|
|
||||||
match settings.ranking_rules {
|
match settings.ranking_rules {
|
||||||
Setting::Set(ref criteria) => {
|
Setting::Set(ref criteria) => {
|
||||||
builder.set_criteria(criteria.iter().map(|c| c.clone().into()).collect())
|
builder.set_ranking_rules(criteria.iter().map(|c| c.clone().into()).collect())
|
||||||
}
|
}
|
||||||
Setting::Reset => builder.reset_criteria(),
|
Setting::Reset => builder.reset_ranking_rules(),
|
||||||
Setting::NotSet => (),
|
Setting::NotSet => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,11 +578,13 @@ pub fn settings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Deserr)]
|
#[derive(Debug, Clone, PartialEq, Eq, Deserr)]
|
||||||
#[deserr(try_from(&String) = FromStr::from_str -> CriterionError)]
|
#[deserr(try_from(&String) = FromStr::from_str -> RankingRuleError)]
|
||||||
pub enum RankingRuleView {
|
pub enum RankingRuleView {
|
||||||
/// 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.
|
||||||
@ -605,7 +607,7 @@ impl Serialize for RankingRuleView {
|
|||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
serializer.serialize_str(&format!("{}", Criterion::from(self.clone())))
|
serializer.serialize_str(&format!("{}", RankingRule::from(self.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'de> Deserialize<'de> for RankingRuleView {
|
impl<'de> Deserialize<'de> for RankingRuleView {
|
||||||
@ -623,7 +625,7 @@ impl<'de> Deserialize<'de> for RankingRuleView {
|
|||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
let criterion = Criterion::from_str(v).map_err(|_| {
|
let criterion = RankingRule::from_str(v).map_err(|_| {
|
||||||
E::invalid_value(serde::de::Unexpected::Str(v), &"a valid ranking rule")
|
E::invalid_value(serde::de::Unexpected::Str(v), &"a valid ranking rule")
|
||||||
})?;
|
})?;
|
||||||
Ok(RankingRuleView::from(criterion))
|
Ok(RankingRuleView::from(criterion))
|
||||||
@ -633,42 +635,44 @@ impl<'de> Deserialize<'de> for RankingRuleView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl FromStr for RankingRuleView {
|
impl FromStr for RankingRuleView {
|
||||||
type Err = <Criterion as FromStr>::Err;
|
type Err = <RankingRule as FromStr>::Err;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(RankingRuleView::from(Criterion::from_str(s)?))
|
Ok(RankingRuleView::from(RankingRule::from_str(s)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for RankingRuleView {
|
impl fmt::Display for RankingRuleView {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
fmt::Display::fmt(&Criterion::from(self.clone()), f)
|
fmt::Display::fmt(&RankingRule::from(self.clone()), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<Criterion> for RankingRuleView {
|
impl From<RankingRule> for RankingRuleView {
|
||||||
fn from(value: Criterion) -> Self {
|
fn from(value: RankingRule) -> Self {
|
||||||
match value {
|
match value {
|
||||||
Criterion::Words => RankingRuleView::Words,
|
RankingRule::Words => RankingRuleView::Words,
|
||||||
Criterion::Typo => RankingRuleView::Typo,
|
RankingRule::FilterBoosting(filter) => RankingRuleView::Boost(filter),
|
||||||
Criterion::Proximity => RankingRuleView::Proximity,
|
RankingRule::Typo => RankingRuleView::Typo,
|
||||||
Criterion::Attribute => RankingRuleView::Attribute,
|
RankingRule::Proximity => RankingRuleView::Proximity,
|
||||||
Criterion::Sort => RankingRuleView::Sort,
|
RankingRule::Attribute => RankingRuleView::Attribute,
|
||||||
Criterion::Exactness => RankingRuleView::Exactness,
|
RankingRule::Sort => RankingRuleView::Sort,
|
||||||
Criterion::Asc(x) => RankingRuleView::Asc(x),
|
RankingRule::Exactness => RankingRuleView::Exactness,
|
||||||
Criterion::Desc(x) => RankingRuleView::Desc(x),
|
RankingRule::Asc(x) => RankingRuleView::Asc(x),
|
||||||
|
RankingRule::Desc(x) => RankingRuleView::Desc(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<RankingRuleView> for Criterion {
|
impl From<RankingRuleView> for RankingRule {
|
||||||
fn from(value: RankingRuleView) -> Self {
|
fn from(value: RankingRuleView) -> Self {
|
||||||
match value {
|
match value {
|
||||||
RankingRuleView::Words => Criterion::Words,
|
RankingRuleView::Words => RankingRule::Words,
|
||||||
RankingRuleView::Typo => Criterion::Typo,
|
RankingRuleView::Boost(filter) => RankingRule::FilterBoosting(filter),
|
||||||
RankingRuleView::Proximity => Criterion::Proximity,
|
RankingRuleView::Typo => RankingRule::Typo,
|
||||||
RankingRuleView::Attribute => Criterion::Attribute,
|
RankingRuleView::Proximity => RankingRule::Proximity,
|
||||||
RankingRuleView::Sort => Criterion::Sort,
|
RankingRuleView::Attribute => RankingRule::Attribute,
|
||||||
RankingRuleView::Exactness => Criterion::Exactness,
|
RankingRuleView::Sort => RankingRule::Sort,
|
||||||
RankingRuleView::Asc(x) => Criterion::Asc(x),
|
RankingRuleView::Exactness => RankingRule::Exactness,
|
||||||
RankingRuleView::Desc(x) => Criterion::Desc(x),
|
RankingRuleView::Asc(x) => RankingRule::Asc(x),
|
||||||
|
RankingRuleView::Desc(x) => RankingRule::Desc(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -629,6 +629,7 @@ impl SearchAggregator {
|
|||||||
show_ranking_score,
|
show_ranking_score,
|
||||||
show_ranking_score_details,
|
show_ranking_score_details,
|
||||||
filter,
|
filter,
|
||||||
|
boosting_filter,
|
||||||
sort,
|
sort,
|
||||||
facets: _,
|
facets: _,
|
||||||
highlight_pre_tag,
|
highlight_pre_tag,
|
||||||
@ -1002,6 +1003,7 @@ impl MultiSearchAggregator {
|
|||||||
show_ranking_score_details: _,
|
show_ranking_score_details: _,
|
||||||
show_matches_position: _,
|
show_matches_position: _,
|
||||||
filter: _,
|
filter: _,
|
||||||
|
boosting_filter: _,
|
||||||
sort: _,
|
sort: _,
|
||||||
facets: _,
|
facets: _,
|
||||||
highlight_pre_tag: _,
|
highlight_pre_tag: _,
|
||||||
|
@ -111,6 +111,7 @@ impl From<FacetSearchQuery> for SearchQuery {
|
|||||||
show_ranking_score: false,
|
show_ranking_score: false,
|
||||||
show_ranking_score_details: false,
|
show_ranking_score_details: false,
|
||||||
filter,
|
filter,
|
||||||
|
boosting_filter: None,
|
||||||
sort: None,
|
sort: None,
|
||||||
facets: None,
|
facets: None,
|
||||||
highlight_pre_tag: DEFAULT_HIGHLIGHT_PRE_TAG(),
|
highlight_pre_tag: DEFAULT_HIGHLIGHT_PRE_TAG(),
|
||||||
|
@ -54,6 +54,8 @@ pub struct SearchQueryGet {
|
|||||||
attributes_to_highlight: Option<CS<String>>,
|
attributes_to_highlight: Option<CS<String>>,
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchFilter>)]
|
#[deserr(default, error = DeserrQueryParamError<InvalidSearchFilter>)]
|
||||||
filter: Option<String>,
|
filter: Option<String>,
|
||||||
|
#[deserr(default, error = DeserrQueryParamError<InvalidSearchBoostingFilter>)]
|
||||||
|
boosting_filter: Option<String>,
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchSort>)]
|
#[deserr(default, error = DeserrQueryParamError<InvalidSearchSort>)]
|
||||||
sort: Option<String>,
|
sort: Option<String>,
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchShowMatchesPosition>)]
|
#[deserr(default, error = DeserrQueryParamError<InvalidSearchShowMatchesPosition>)]
|
||||||
@ -86,6 +88,14 @@ impl From<SearchQueryGet> for SearchQuery {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let boosting_filter = match other.boosting_filter {
|
||||||
|
Some(f) => match serde_json::from_str(&f) {
|
||||||
|
Ok(v) => Some(v),
|
||||||
|
_ => Some(Value::String(f)),
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
q: other.q,
|
q: other.q,
|
||||||
vector: other.vector.map(CS::into_inner),
|
vector: other.vector.map(CS::into_inner),
|
||||||
@ -98,6 +108,7 @@ impl From<SearchQueryGet> for SearchQuery {
|
|||||||
crop_length: other.crop_length.0,
|
crop_length: other.crop_length.0,
|
||||||
attributes_to_highlight: other.attributes_to_highlight.map(|o| o.into_iter().collect()),
|
attributes_to_highlight: other.attributes_to_highlight.map(|o| o.into_iter().collect()),
|
||||||
filter,
|
filter,
|
||||||
|
boosting_filter,
|
||||||
sort: other.sort.map(|attr| fix_sort_query_parameters(&attr)),
|
sort: other.sort.map(|attr| fix_sort_query_parameters(&attr)),
|
||||||
show_matches_position: other.show_matches_position.0,
|
show_matches_position: other.show_matches_position.0,
|
||||||
show_ranking_score: other.show_ranking_score.0,
|
show_ranking_score: other.show_ranking_score.0,
|
||||||
|
@ -71,6 +71,8 @@ pub struct SearchQuery {
|
|||||||
pub show_ranking_score_details: bool,
|
pub show_ranking_score_details: bool,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
||||||
pub filter: Option<Value>,
|
pub filter: Option<Value>,
|
||||||
|
#[deserr(default, error = DeserrJsonError<InvalidSearchBoostingFilter>)]
|
||||||
|
pub boosting_filter: Option<Value>,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
||||||
pub sort: Option<Vec<String>>,
|
pub sort: Option<Vec<String>>,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
||||||
@ -130,6 +132,8 @@ pub struct SearchQueryWithIndex {
|
|||||||
pub show_matches_position: bool,
|
pub show_matches_position: bool,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
||||||
pub filter: Option<Value>,
|
pub filter: Option<Value>,
|
||||||
|
#[deserr(default, error = DeserrJsonError<InvalidSearchBoostingFilter>)]
|
||||||
|
pub boosting_filter: Option<Value>,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
||||||
pub sort: Option<Vec<String>>,
|
pub sort: Option<Vec<String>>,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
||||||
@ -164,6 +168,7 @@ impl SearchQueryWithIndex {
|
|||||||
show_ranking_score_details,
|
show_ranking_score_details,
|
||||||
show_matches_position,
|
show_matches_position,
|
||||||
filter,
|
filter,
|
||||||
|
boosting_filter,
|
||||||
sort,
|
sort,
|
||||||
facets,
|
facets,
|
||||||
highlight_pre_tag,
|
highlight_pre_tag,
|
||||||
@ -189,6 +194,7 @@ impl SearchQueryWithIndex {
|
|||||||
show_ranking_score_details,
|
show_ranking_score_details,
|
||||||
show_matches_position,
|
show_matches_position,
|
||||||
filter,
|
filter,
|
||||||
|
boosting_filter,
|
||||||
sort,
|
sort,
|
||||||
facets,
|
facets,
|
||||||
highlight_pre_tag,
|
highlight_pre_tag,
|
||||||
@ -397,8 +403,14 @@ fn prepare_search<'t>(
|
|||||||
search.limit(limit);
|
search.limit(limit);
|
||||||
|
|
||||||
if let Some(ref filter) = query.filter {
|
if let Some(ref filter) = query.filter {
|
||||||
if let Some(facets) = parse_filter(filter)? {
|
if let Some(filter) = parse_filter(filter)? {
|
||||||
search.filter(facets);
|
search.filter(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref boosting_filter) = query.boosting_filter {
|
||||||
|
if let Some(boosting_filter) = parse_filter(boosting_filter)? {
|
||||||
|
search.boosting_filter(boosting_filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -3,7 +3,7 @@ use heed::EnvOpenOptions;
|
|||||||
// use maplit::hashset;
|
// use maplit::hashset;
|
||||||
use milli::{
|
use milli::{
|
||||||
update::{IndexerConfig, Settings},
|
update::{IndexerConfig, Settings},
|
||||||
Criterion, Index,
|
Index, RankingRule,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -19,13 +19,13 @@ fn main() {
|
|||||||
// builder.set_min_word_len_one_typo(5);
|
// builder.set_min_word_len_one_typo(5);
|
||||||
// builder.set_min_word_len_two_typos(7);
|
// builder.set_min_word_len_two_typos(7);
|
||||||
// builder.set_sortable_fields(hashset! { S("release_date") });
|
// builder.set_sortable_fields(hashset! { S("release_date") });
|
||||||
builder.set_criteria(vec![
|
builder.set_ranking_rules(vec![
|
||||||
Criterion::Words,
|
RankingRule::Words,
|
||||||
Criterion::Typo,
|
RankingRule::Typo,
|
||||||
Criterion::Proximity,
|
RankingRule::Proximity,
|
||||||
Criterion::Attribute,
|
RankingRule::Attribute,
|
||||||
Criterion::Sort,
|
RankingRule::Sort,
|
||||||
Criterion::Exactness,
|
RankingRule::Exactness,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
builder.execute(|_| (), || false).unwrap();
|
builder.execute(|_| (), || false).unwrap();
|
||||||
|
@ -8,7 +8,7 @@ use thiserror::Error;
|
|||||||
|
|
||||||
use crate::error::is_reserved_keyword;
|
use crate::error::is_reserved_keyword;
|
||||||
use crate::search::facet::BadGeoError;
|
use crate::search::facet::BadGeoError;
|
||||||
use crate::{CriterionError, Error, UserError};
|
use crate::{Error, RankingRuleError, UserError};
|
||||||
|
|
||||||
/// This error type is never supposed to be shown to the end user.
|
/// 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.
|
/// You must always cast it to a sort error or a criterion error.
|
||||||
@ -28,23 +28,23 @@ impl From<BadGeoError> for AscDescError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AscDescError> for CriterionError {
|
impl From<AscDescError> for RankingRuleError {
|
||||||
fn from(error: AscDescError) -> Self {
|
fn from(error: AscDescError) -> Self {
|
||||||
match error {
|
match error {
|
||||||
AscDescError::GeoError(_) => {
|
AscDescError::GeoError(_) => {
|
||||||
CriterionError::ReservedNameForSort { name: "_geoPoint".to_string() }
|
RankingRuleError::ReservedNameForSort { name: "_geoPoint".to_string() }
|
||||||
}
|
}
|
||||||
AscDescError::InvalidSyntax { name } => CriterionError::InvalidName { name },
|
AscDescError::InvalidSyntax { name } => RankingRuleError::InvalidName { name },
|
||||||
AscDescError::ReservedKeyword { name } if name.starts_with("_geoPoint") => {
|
AscDescError::ReservedKeyword { name } if name.starts_with("_geoPoint") => {
|
||||||
CriterionError::ReservedNameForSort { name: "_geoPoint".to_string() }
|
RankingRuleError::ReservedNameForSort { name: "_geoPoint".to_string() }
|
||||||
}
|
}
|
||||||
AscDescError::ReservedKeyword { name } if name.starts_with("_geoRadius") => {
|
AscDescError::ReservedKeyword { name } if name.starts_with("_geoRadius") => {
|
||||||
CriterionError::ReservedNameForFilter { name: "_geoRadius".to_string() }
|
RankingRuleError::ReservedNameForFilter { name: "_geoRadius".to_string() }
|
||||||
}
|
}
|
||||||
AscDescError::ReservedKeyword { name } if name.starts_with("_geoBoundingBox") => {
|
AscDescError::ReservedKeyword { name } if name.starts_with("_geoBoundingBox") => {
|
||||||
CriterionError::ReservedNameForFilter { name: "_geoBoundingBox".to_string() }
|
RankingRuleError::ReservedNameForFilter { name: "_geoBoundingBox".to_string() }
|
||||||
}
|
}
|
||||||
AscDescError::ReservedKeyword { name } => CriterionError::ReservedName { name },
|
AscDescError::ReservedKeyword { name } => RankingRuleError::ReservedName { name },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use serde_json::Value;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::documents::{self, DocumentsBatchCursorError};
|
use crate::documents::{self, DocumentsBatchCursorError};
|
||||||
use crate::{CriterionError, DocumentId, FieldId, Object, SortError};
|
use crate::{DocumentId, FieldId, Object, RankingRuleError, SortError};
|
||||||
|
|
||||||
pub fn is_reserved_keyword(keyword: &str) -> bool {
|
pub fn is_reserved_keyword(keyword: &str) -> bool {
|
||||||
["_geo", "_geoDistance", "_geoPoint", "_geoRadius", "_geoBoundingBox"].contains(&keyword)
|
["_geo", "_geoDistance", "_geoPoint", "_geoRadius", "_geoBoundingBox"].contains(&keyword)
|
||||||
@ -94,7 +94,7 @@ pub enum UserError {
|
|||||||
#[error("A document cannot contain more than 65,535 fields.")]
|
#[error("A document cannot contain more than 65,535 fields.")]
|
||||||
AttributeLimitReached,
|
AttributeLimitReached,
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
CriterionError(#[from] CriterionError),
|
CriterionError(#[from] RankingRuleError),
|
||||||
#[error("Maximum number of documents reached.")]
|
#[error("Maximum number of documents reached.")]
|
||||||
DocumentLimitReached,
|
DocumentLimitReached,
|
||||||
#[error(
|
#[error(
|
||||||
@ -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. {}",
|
||||||
@ -280,7 +282,7 @@ error_from_sub_error! {
|
|||||||
ThreadPoolBuildError => InternalError,
|
ThreadPoolBuildError => InternalError,
|
||||||
SerializationError => InternalError,
|
SerializationError => InternalError,
|
||||||
GeoError => UserError,
|
GeoError => UserError,
|
||||||
CriterionError => UserError,
|
RankingRuleError => UserError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> From<grenad::Error<E>> for Error
|
impl<E> From<grenad::Error<E>> for Error
|
||||||
|
@ -25,10 +25,9 @@ use crate::heed_codec::{
|
|||||||
};
|
};
|
||||||
use crate::readable_slices::ReadableSlices;
|
use crate::readable_slices::ReadableSlices;
|
||||||
use crate::{
|
use crate::{
|
||||||
default_criteria, CboRoaringBitmapCodec, Criterion, DocumentId, ExternalDocumentsIds,
|
default_criteria, CboRoaringBitmapCodec, DocumentId, ExternalDocumentsIds, FacetDistribution,
|
||||||
FacetDistribution, FieldDistribution, FieldId, FieldIdWordCountCodec, GeoPoint, ObkvCodec,
|
FieldDistribution, FieldId, FieldIdWordCountCodec, GeoPoint, ObkvCodec, OrderBy, RankingRule,
|
||||||
OrderBy, Result, RoaringBitmapCodec, RoaringBitmapLenCodec, Search, U8StrStrCodec, BEU16,
|
Result, RoaringBitmapCodec, RoaringBitmapLenCodec, Search, U8StrStrCodec, BEU16, BEU32,
|
||||||
BEU32,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The HNSW data-structure that we serialize, fill and search in.
|
/// The HNSW data-structure that we serialize, fill and search in.
|
||||||
@ -895,7 +894,7 @@ impl Index {
|
|||||||
let distinct_field = self.distinct_field(rtxn)?;
|
let distinct_field = self.distinct_field(rtxn)?;
|
||||||
let asc_desc_fields =
|
let asc_desc_fields =
|
||||||
self.criteria(rtxn)?.into_iter().filter_map(|criterion| match criterion {
|
self.criteria(rtxn)?.into_iter().filter_map(|criterion| match criterion {
|
||||||
Criterion::Asc(field) | Criterion::Desc(field) => Some(field),
|
RankingRule::Asc(field) | RankingRule::Desc(field) => Some(field),
|
||||||
_otherwise => None,
|
_otherwise => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1023,17 +1022,17 @@ impl Index {
|
|||||||
pub(crate) fn put_criteria(
|
pub(crate) fn put_criteria(
|
||||||
&self,
|
&self,
|
||||||
wtxn: &mut RwTxn,
|
wtxn: &mut RwTxn,
|
||||||
criteria: &[Criterion],
|
criteria: &[RankingRule],
|
||||||
) -> heed::Result<()> {
|
) -> heed::Result<()> {
|
||||||
self.main.put::<_, Str, SerdeJson<&[Criterion]>>(wtxn, main_key::CRITERIA_KEY, &criteria)
|
self.main.put::<_, Str, SerdeJson<&[RankingRule]>>(wtxn, main_key::CRITERIA_KEY, &criteria)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn delete_criteria(&self, wtxn: &mut RwTxn) -> heed::Result<bool> {
|
pub(crate) fn delete_criteria(&self, wtxn: &mut RwTxn) -> heed::Result<bool> {
|
||||||
self.main.delete::<_, Str>(wtxn, main_key::CRITERIA_KEY)
|
self.main.delete::<_, Str>(wtxn, main_key::CRITERIA_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn criteria(&self, rtxn: &RoTxn) -> heed::Result<Vec<Criterion>> {
|
pub fn criteria(&self, rtxn: &RoTxn) -> heed::Result<Vec<RankingRule>> {
|
||||||
match self.main.get::<_, Str, SerdeJson<Vec<Criterion>>>(rtxn, main_key::CRITERIA_KEY)? {
|
match self.main.get::<_, Str, SerdeJson<Vec<RankingRule>>>(rtxn, main_key::CRITERIA_KEY)? {
|
||||||
Some(criteria) => Ok(criteria),
|
Some(criteria) => Ok(criteria),
|
||||||
None => Ok(default_criteria()),
|
None => Ok(default_criteria()),
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ pub static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
|||||||
pub mod documents;
|
pub mod documents;
|
||||||
|
|
||||||
mod asc_desc;
|
mod asc_desc;
|
||||||
mod criterion;
|
|
||||||
pub mod distance;
|
pub mod distance;
|
||||||
mod error;
|
mod error;
|
||||||
mod external_documents_ids;
|
mod external_documents_ids;
|
||||||
@ -18,6 +17,7 @@ mod fields_ids_map;
|
|||||||
pub mod heed_codec;
|
pub mod heed_codec;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
pub mod proximity;
|
pub mod proximity;
|
||||||
|
mod ranking_rule;
|
||||||
mod readable_slices;
|
mod readable_slices;
|
||||||
pub mod score_details;
|
pub mod score_details;
|
||||||
mod search;
|
mod search;
|
||||||
@ -44,7 +44,6 @@ use serde_json::Value;
|
|||||||
pub use {charabia as tokenizer, heed};
|
pub use {charabia as tokenizer, heed};
|
||||||
|
|
||||||
pub use self::asc_desc::{AscDesc, AscDescError, Member, SortError};
|
pub use self::asc_desc::{AscDesc, AscDescError, Member, SortError};
|
||||||
pub use self::criterion::{default_criteria, Criterion, CriterionError};
|
|
||||||
pub use self::error::{
|
pub use self::error::{
|
||||||
Error, FieldIdMapMissingEntry, InternalError, SerializationError, UserError,
|
Error, FieldIdMapMissingEntry, InternalError, SerializationError, UserError,
|
||||||
};
|
};
|
||||||
@ -57,6 +56,7 @@ pub use self::heed_codec::{
|
|||||||
UncheckedU8StrStrCodec,
|
UncheckedU8StrStrCodec,
|
||||||
};
|
};
|
||||||
pub use self::index::Index;
|
pub use self::index::Index;
|
||||||
|
pub use self::ranking_rule::{default_criteria, RankingRule, RankingRuleError};
|
||||||
pub use self::search::{
|
pub use self::search::{
|
||||||
FacetDistribution, FacetValueHit, Filter, FormatOptions, MatchBounds, MatcherBuilder,
|
FacetDistribution, FacetValueHit, Filter, FormatOptions, MatchBounds, MatcherBuilder,
|
||||||
MatchingWords, OrderBy, Search, SearchForFacetValues, SearchResult, TermsMatchingStrategy,
|
MatchingWords, OrderBy, Search, SearchForFacetValues, SearchResult, TermsMatchingStrategy,
|
||||||
|
@ -7,7 +7,7 @@ use thiserror::Error;
|
|||||||
use crate::{AscDesc, Member};
|
use crate::{AscDesc, Member};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum CriterionError {
|
pub enum RankingRuleError {
|
||||||
#[error("`{name}` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.")]
|
#[error("`{name}` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.")]
|
||||||
InvalidName { name: String },
|
InvalidName { name: String },
|
||||||
#[error("`{name}` is a reserved keyword and thus can't be used as a ranking rule")]
|
#[error("`{name}` is a reserved keyword and thus can't be used as a ranking rule")]
|
||||||
@ -25,7 +25,9 @@ pub enum CriterionError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
pub enum Criterion {
|
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,
|
||||||
@ -47,62 +49,68 @@ pub enum Criterion {
|
|||||||
Desc(String),
|
Desc(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Criterion {
|
impl RankingRule {
|
||||||
/// Returns the field name parameter of this criterion.
|
/// Returns the field name parameter of this criterion.
|
||||||
pub fn field_name(&self) -> Option<&str> {
|
pub fn field_name(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
Criterion::Asc(name) | Criterion::Desc(name) => Some(name),
|
RankingRule::Asc(name) | RankingRule::Desc(name) => Some(name),
|
||||||
_otherwise => None,
|
_otherwise => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Criterion {
|
impl FromStr for RankingRule {
|
||||||
type Err = CriterionError;
|
type Err = RankingRuleError;
|
||||||
|
|
||||||
fn from_str(text: &str) -> Result<Criterion, Self::Err> {
|
fn from_str(text: &str) -> Result<RankingRule, Self::Err> {
|
||||||
match text {
|
match text {
|
||||||
"words" => Ok(Criterion::Words),
|
"words" => Ok(RankingRule::Words),
|
||||||
"typo" => Ok(Criterion::Typo),
|
"typo" => Ok(RankingRule::Typo),
|
||||||
"proximity" => Ok(Criterion::Proximity),
|
"proximity" => Ok(RankingRule::Proximity),
|
||||||
"attribute" => Ok(Criterion::Attribute),
|
"attribute" => Ok(RankingRule::Attribute),
|
||||||
"sort" => Ok(Criterion::Sort),
|
"sort" => Ok(RankingRule::Sort),
|
||||||
"exactness" => Ok(Criterion::Exactness),
|
"exactness" => Ok(RankingRule::Exactness),
|
||||||
text => match AscDesc::from_str(text)? {
|
text => match AscDesc::from_str(text) {
|
||||||
AscDesc::Asc(Member::Field(field)) => Ok(Criterion::Asc(field)),
|
Ok(asc_desc) => match asc_desc {
|
||||||
AscDesc::Desc(Member::Field(field)) => Ok(Criterion::Desc(field)),
|
AscDesc::Asc(Member::Field(field)) => Ok(RankingRule::Asc(field)),
|
||||||
AscDesc::Asc(Member::Geo(_)) | AscDesc::Desc(Member::Geo(_)) => {
|
AscDesc::Desc(Member::Field(field)) => Ok(RankingRule::Desc(field)),
|
||||||
Err(CriterionError::ReservedNameForSort { name: "_geoPoint".to_string() })?
|
AscDesc::Asc(Member::Geo(_)) | AscDesc::Desc(Member::Geo(_)) => {
|
||||||
}
|
Err(RankingRuleError::ReservedNameForSort {
|
||||||
|
name: "_geoPoint".to_string(),
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => Err(err.into()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_criteria() -> Vec<Criterion> {
|
pub fn default_criteria() -> Vec<RankingRule> {
|
||||||
vec![
|
vec![
|
||||||
Criterion::Words,
|
RankingRule::Words,
|
||||||
Criterion::Typo,
|
RankingRule::Typo,
|
||||||
Criterion::Proximity,
|
RankingRule::Proximity,
|
||||||
Criterion::Attribute,
|
RankingRule::Attribute,
|
||||||
Criterion::Sort,
|
RankingRule::Sort,
|
||||||
Criterion::Exactness,
|
RankingRule::Exactness,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Criterion {
|
impl fmt::Display for RankingRule {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use Criterion::*;
|
use RankingRule::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Words => f.write_str("words"),
|
Words => f.write_str("words"),
|
||||||
|
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"),
|
||||||
Sort => f.write_str("sort"),
|
Sort => f.write_str("sort"),
|
||||||
Exactness => f.write_str("exactness"),
|
Exactness => f.write_str("exactness"),
|
||||||
Asc(attr) => write!(f, "{}:asc", attr),
|
Asc(attr) => write!(f, "{attr}:asc"),
|
||||||
Desc(attr) => write!(f, "{}:desc", attr),
|
Desc(attr) => write!(f, "{attr}:desc"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,29 +118,29 @@ impl fmt::Display for Criterion {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use big_s::S;
|
use big_s::S;
|
||||||
use CriterionError::*;
|
use RankingRuleError::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_criterion() {
|
fn parse_criterion() {
|
||||||
let valid_criteria = [
|
let valid_criteria = [
|
||||||
("words", Criterion::Words),
|
("words", RankingRule::Words),
|
||||||
("typo", Criterion::Typo),
|
("typo", RankingRule::Typo),
|
||||||
("proximity", Criterion::Proximity),
|
("proximity", RankingRule::Proximity),
|
||||||
("attribute", Criterion::Attribute),
|
("attribute", RankingRule::Attribute),
|
||||||
("sort", Criterion::Sort),
|
("sort", RankingRule::Sort),
|
||||||
("exactness", Criterion::Exactness),
|
("exactness", RankingRule::Exactness),
|
||||||
("price:asc", Criterion::Asc(S("price"))),
|
("price:asc", RankingRule::Asc(S("price"))),
|
||||||
("price:desc", Criterion::Desc(S("price"))),
|
("price:desc", RankingRule::Desc(S("price"))),
|
||||||
("price:asc:desc", Criterion::Desc(S("price:asc"))),
|
("price:asc:desc", RankingRule::Desc(S("price:asc"))),
|
||||||
("truc:machin:desc", Criterion::Desc(S("truc:machin"))),
|
("truc:machin:desc", RankingRule::Desc(S("truc:machin"))),
|
||||||
("hello-world!:desc", Criterion::Desc(S("hello-world!"))),
|
("hello-world!:desc", RankingRule::Desc(S("hello-world!"))),
|
||||||
("it's spacy over there:asc", Criterion::Asc(S("it's spacy over there"))),
|
("it's spacy over there:asc", RankingRule::Asc(S("it's spacy over there"))),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (input, expected) in valid_criteria {
|
for (input, expected) in valid_criteria {
|
||||||
let res = input.parse::<Criterion>();
|
let res = input.parse::<RankingRule>();
|
||||||
assert!(
|
assert!(
|
||||||
res.is_ok(),
|
res.is_ok(),
|
||||||
"Failed to parse `{}`, was expecting `{:?}` but instead got `{:?}`",
|
"Failed to parse `{}`, was expecting `{:?}` but instead got `{:?}`",
|
||||||
@ -167,7 +175,7 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (input, expected) in invalid_criteria {
|
for (input, expected) in invalid_criteria {
|
||||||
let res = input.parse::<Criterion>();
|
let res = input.parse::<RankingRule>();
|
||||||
assert!(
|
assert!(
|
||||||
res.is_err(),
|
res.is_err(),
|
||||||
"Should no be able to parse `{}`, was expecting an error but instead got: `{:?}`",
|
"Should no be able to parse `{}`, was expecting an error but instead got: `{:?}`",
|
@ -5,6 +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),
|
||||||
|
FilterBoosting(FilterBoosting),
|
||||||
Typo(Typo),
|
Typo(Typo),
|
||||||
Proximity(Rank),
|
Proximity(Rank),
|
||||||
Fid(Rank),
|
Fid(Rank),
|
||||||
@ -23,6 +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::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),
|
||||||
@ -60,6 +62,11 @@ impl ScoreDetails {
|
|||||||
details_map.insert("words".into(), words_details);
|
details_map.insert("words".into(), words_details);
|
||||||
order += 1;
|
order += 1;
|
||||||
}
|
}
|
||||||
|
ScoreDetails::FilterBoosting(FilterBoosting { matching }) => {
|
||||||
|
let sort_details = serde_json::json!({ "matching": matching });
|
||||||
|
details_map.insert("filterBoosting".into(), sort_details);
|
||||||
|
order += 1;
|
||||||
|
}
|
||||||
ScoreDetails::Typo(typo) => {
|
ScoreDetails::Typo(typo) => {
|
||||||
let typo_details = serde_json::json!({
|
let typo_details = serde_json::json!({
|
||||||
"order": order,
|
"order": order,
|
||||||
@ -221,6 +228,11 @@ impl Words {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct FilterBoosting {
|
||||||
|
pub matching: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Structure that is super similar to [`Words`], but whose semantics is a bit distinct.
|
/// Structure that is super similar to [`Words`], but whose semantics is a bit distinct.
|
||||||
///
|
///
|
||||||
/// In exactness, the number of matching words can actually be 0 with a non-zero score,
|
/// In exactness, the number of matching words can actually be 0 with a non-zero score,
|
||||||
|
@ -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)
|
||||||
|
79
milli/src/search/new/filter_boosting.rs
Normal file
79
milli/src/search/new/filter_boosting.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use roaring::RoaringBitmap;
|
||||||
|
|
||||||
|
use super::logger::SearchLogger;
|
||||||
|
use super::{RankingRule, RankingRuleOutput, RankingRuleQueryTrait, SearchContext};
|
||||||
|
use crate::score_details::{self, ScoreDetails};
|
||||||
|
use crate::{Filter, Result};
|
||||||
|
|
||||||
|
pub struct FilterBoosting<'f, Query> {
|
||||||
|
filter: Filter<'f>,
|
||||||
|
original_query: Option<Query>,
|
||||||
|
matching: Option<RankingRuleOutput<Query>>,
|
||||||
|
non_matching: Option<RankingRuleOutput<Query>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'f, Query> FilterBoosting<'f, Query> {
|
||||||
|
pub fn new(filter: Filter<'f>) -> Result<Self> {
|
||||||
|
Ok(Self { filter, original_query: None, matching: None, non_matching: None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'f, Query: RankingRuleQueryTrait> RankingRule<'ctx, Query>
|
||||||
|
for FilterBoosting<'f, Query>
|
||||||
|
{
|
||||||
|
fn id(&self) -> String {
|
||||||
|
// TODO improve this
|
||||||
|
let Self { filter: original_expression, .. } = self;
|
||||||
|
format!("boost:{original_expression:?}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_iteration(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut SearchContext<'ctx>,
|
||||||
|
_logger: &mut dyn SearchLogger<Query>,
|
||||||
|
parent_candidates: &RoaringBitmap,
|
||||||
|
parent_query: &Query,
|
||||||
|
) -> Result<()> {
|
||||||
|
let universe_matching = match self.filter.evaluate(ctx.txn, ctx.index) {
|
||||||
|
Ok(documents) => documents,
|
||||||
|
Err(e) => return Err(e), // TODO manage the invalid_search_boosting_filter
|
||||||
|
};
|
||||||
|
let matching = parent_candidates & universe_matching;
|
||||||
|
let non_matching = parent_candidates - &matching;
|
||||||
|
|
||||||
|
self.original_query = Some(parent_query.clone());
|
||||||
|
|
||||||
|
self.matching = Some(RankingRuleOutput {
|
||||||
|
query: parent_query.clone(),
|
||||||
|
candidates: matching,
|
||||||
|
score: ScoreDetails::FilterBoosting(score_details::FilterBoosting { matching: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.non_matching = Some(RankingRuleOutput {
|
||||||
|
query: parent_query.clone(),
|
||||||
|
candidates: non_matching,
|
||||||
|
score: ScoreDetails::FilterBoosting(score_details::FilterBoosting { matching: false }),
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_bucket(
|
||||||
|
&mut self,
|
||||||
|
_ctx: &mut SearchContext<'ctx>,
|
||||||
|
_logger: &mut dyn SearchLogger<Query>,
|
||||||
|
_universe: &RoaringBitmap,
|
||||||
|
) -> Result<Option<RankingRuleOutput<Query>>> {
|
||||||
|
Ok(self.matching.take().or_else(|| self.non_matching.take()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end_iteration(
|
||||||
|
&mut self,
|
||||||
|
_ctx: &mut SearchContext<'ctx>,
|
||||||
|
_logger: &mut dyn SearchLogger<Query>,
|
||||||
|
) {
|
||||||
|
self.original_query = None;
|
||||||
|
self.matching = None;
|
||||||
|
self.non_matching = None;
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -15,6 +15,7 @@ mod resolve_query_graph;
|
|||||||
mod small_bitmap;
|
mod small_bitmap;
|
||||||
|
|
||||||
mod exact_attribute;
|
mod exact_attribute;
|
||||||
|
mod filter_boosting;
|
||||||
mod sort;
|
mod sort;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -26,6 +27,7 @@ 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;
|
||||||
@ -190,25 +192,30 @@ 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::Criterion::Words
|
crate::RankingRule::FilterBoosting(_)
|
||||||
| crate::Criterion::Typo
|
| crate::RankingRule::Words
|
||||||
| crate::Criterion::Attribute
|
| crate::RankingRule::Typo
|
||||||
| crate::Criterion::Proximity
|
| crate::RankingRule::Attribute
|
||||||
| crate::Criterion::Exactness => continue,
|
| crate::RankingRule::Proximity
|
||||||
crate::Criterion::Sort => {
|
| crate::RankingRule::Exactness => continue,
|
||||||
|
crate::RankingRule::Sort => {
|
||||||
if sort {
|
if sort {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -222,14 +229,14 @@ fn get_ranking_rules_for_placeholder_search<'ctx>(
|
|||||||
)?;
|
)?;
|
||||||
sort = true;
|
sort = true;
|
||||||
}
|
}
|
||||||
crate::Criterion::Asc(field_name) => {
|
crate::RankingRule::Asc(field_name) => {
|
||||||
if sorted_fields.contains(&field_name) {
|
if sorted_fields.contains(&field_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sorted_fields.insert(field_name.clone());
|
sorted_fields.insert(field_name.clone());
|
||||||
ranking_rules.push(Box::new(Sort::new(ctx.index, ctx.txn, field_name, true)?));
|
ranking_rules.push(Box::new(Sort::new(ctx.index, ctx.txn, field_name, true)?));
|
||||||
}
|
}
|
||||||
crate::Criterion::Desc(field_name) => {
|
crate::RankingRule::Desc(field_name) => {
|
||||||
if sorted_fields.contains(&field_name) {
|
if sorted_fields.contains(&field_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -242,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;
|
||||||
@ -263,15 +271,18 @@ 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
|
||||||
match rr {
|
match rr {
|
||||||
crate::Criterion::Typo
|
crate::RankingRule::Typo
|
||||||
| crate::Criterion::Attribute
|
| crate::RankingRule::Attribute
|
||||||
| crate::Criterion::Proximity
|
| crate::RankingRule::Proximity
|
||||||
| crate::Criterion::Exactness => {
|
| crate::RankingRule::Exactness => {
|
||||||
if !words {
|
if !words {
|
||||||
ranking_rules.push(Box::new(Words::new(terms_matching_strategy)));
|
ranking_rules.push(Box::new(Words::new(terms_matching_strategy)));
|
||||||
words = true;
|
words = true;
|
||||||
@ -280,28 +291,33 @@ fn get_ranking_rules_for_query_graph_search<'ctx>(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
match rr {
|
match rr {
|
||||||
crate::Criterion::Words => {
|
crate::RankingRule::Words => {
|
||||||
if words {
|
if words {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
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::Criterion::Typo => {
|
crate::RankingRule::FilterBoosting(_) => {
|
||||||
|
// 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 => {
|
||||||
if typo {
|
if typo {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
typo = true;
|
typo = true;
|
||||||
ranking_rules.push(Box::new(Typo::new(None)));
|
ranking_rules.push(Box::new(Typo::new(None)));
|
||||||
}
|
}
|
||||||
crate::Criterion::Proximity => {
|
crate::RankingRule::Proximity => {
|
||||||
if proximity {
|
if proximity {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
proximity = true;
|
proximity = true;
|
||||||
ranking_rules.push(Box::new(Proximity::new(None)));
|
ranking_rules.push(Box::new(Proximity::new(None)));
|
||||||
}
|
}
|
||||||
crate::Criterion::Attribute => {
|
crate::RankingRule::Attribute => {
|
||||||
if attribute {
|
if attribute {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -309,7 +325,7 @@ fn get_ranking_rules_for_query_graph_search<'ctx>(
|
|||||||
ranking_rules.push(Box::new(Fid::new(None)));
|
ranking_rules.push(Box::new(Fid::new(None)));
|
||||||
ranking_rules.push(Box::new(Position::new(None)));
|
ranking_rules.push(Box::new(Position::new(None)));
|
||||||
}
|
}
|
||||||
crate::Criterion::Sort => {
|
crate::RankingRule::Sort => {
|
||||||
if sort {
|
if sort {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -323,7 +339,7 @@ fn get_ranking_rules_for_query_graph_search<'ctx>(
|
|||||||
)?;
|
)?;
|
||||||
sort = true;
|
sort = true;
|
||||||
}
|
}
|
||||||
crate::Criterion::Exactness => {
|
crate::RankingRule::Exactness => {
|
||||||
if exactness {
|
if exactness {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -331,14 +347,15 @@ fn get_ranking_rules_for_query_graph_search<'ctx>(
|
|||||||
ranking_rules.push(Box::new(Exactness::new()));
|
ranking_rules.push(Box::new(Exactness::new()));
|
||||||
exactness = true;
|
exactness = true;
|
||||||
}
|
}
|
||||||
crate::Criterion::Asc(field_name) => {
|
crate::RankingRule::Asc(field_name) => {
|
||||||
|
// TODO Question: Why would it be invalid to sort price:asc, typo, price:desc?
|
||||||
if sorted_fields.contains(&field_name) {
|
if sorted_fields.contains(&field_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sorted_fields.insert(field_name.clone());
|
sorted_fields.insert(field_name.clone());
|
||||||
ranking_rules.push(Box::new(Sort::new(ctx.index, ctx.txn, field_name, true)?));
|
ranking_rules.push(Box::new(Sort::new(ctx.index, ctx.txn, field_name, true)?));
|
||||||
}
|
}
|
||||||
crate::Criterion::Desc(field_name) => {
|
crate::RankingRule::Desc(field_name) => {
|
||||||
if sorted_fields.contains(&field_name) {
|
if sorted_fields.contains(&field_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -406,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,
|
||||||
@ -422,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)?
|
||||||
};
|
};
|
||||||
@ -516,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 =
|
||||||
@ -532,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,
|
||||||
@ -580,7 +604,8 @@ fn check_sort_criteria(ctx: &SearchContext, sort_criteria: Option<&Vec<AscDesc>>
|
|||||||
|
|
||||||
// We check that the sort ranking rule exists and throw an
|
// We check that the sort ranking rule exists and throw an
|
||||||
// error if we try to use it and that it doesn't.
|
// error if we try to use it and that it doesn't.
|
||||||
let sort_ranking_rule_missing = !ctx.index.criteria(ctx.txn)?.contains(&crate::Criterion::Sort);
|
let sort_ranking_rule_missing =
|
||||||
|
!ctx.index.criteria(ctx.txn)?.contains(&crate::RankingRule::Sort);
|
||||||
if sort_ranking_rule_missing {
|
if sort_ranking_rule_missing {
|
||||||
return Err(UserError::SortRankingRuleMissing.into());
|
return Err(UserError::SortRankingRuleMissing.into());
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -12,7 +12,7 @@ fn create_index() -> TempIndex {
|
|||||||
"description".to_owned(),
|
"description".to_owned(),
|
||||||
"plot".to_owned(),
|
"plot".to_owned(),
|
||||||
]);
|
]);
|
||||||
s.set_criteria(vec![Criterion::Attribute]);
|
s.set_ranking_rules(vec![RankingRule::Attribute]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::{db_snap, Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{db_snap, RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -12,7 +12,7 @@ fn create_index() -> TempIndex {
|
|||||||
"text2".to_owned(),
|
"text2".to_owned(),
|
||||||
"other".to_owned(),
|
"other".to_owned(),
|
||||||
]);
|
]);
|
||||||
s.set_criteria(vec![Criterion::Attribute]);
|
s.set_ranking_rules(vec![RankingRule::Attribute]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ use maplit::hashset;
|
|||||||
|
|
||||||
use super::collect_field_values;
|
use super::collect_field_values;
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::{AscDesc, Criterion, Index, Member, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{AscDesc, Index, Member, RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -30,7 +30,7 @@ fn create_index() -> TempIndex {
|
|||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_sortable_fields(hashset! { S("rank1"), S("letter") });
|
s.set_sortable_fields(hashset! { S("rank1"), S("letter") });
|
||||||
s.set_distinct_field("letter".to_owned());
|
s.set_distinct_field("letter".to_owned());
|
||||||
s.set_criteria(vec![Criterion::Words]);
|
s.set_ranking_rules(vec![RankingRule::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ fn test_distinct_placeholder_sort() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Sort]);
|
s.set_ranking_rules(vec![RankingRule::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -387,7 +387,7 @@ fn test_distinct_words() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Words]);
|
s.set_ranking_rules(vec![RankingRule::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -440,7 +440,11 @@ fn test_distinct_sort_words() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Sort, Criterion::Words, Criterion::Desc(S("rank1"))]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Sort,
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Desc(S("rank1")),
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -513,7 +517,7 @@ fn test_distinct_all_candidates() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Sort]);
|
s.set_ranking_rules(vec![RankingRule::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -536,7 +540,7 @@ fn test_distinct_typo() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Typo]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Typo]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ Then these rules will only work with
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index_simple_ordered() -> TempIndex {
|
fn create_index_simple_ordered() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -30,7 +30,7 @@ fn create_index_simple_ordered() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Exactness]);
|
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ fn create_index_simple_reversed() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Exactness]);
|
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ fn create_index_simple_random() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Exactness]);
|
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ fn create_index_attribute_starts_with() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Exactness]);
|
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ fn create_index_simple_ordered_with_typos() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Exactness]);
|
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -350,7 +350,11 @@ fn create_index_with_varying_proximities() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Exactness,
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Proximity,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -404,7 +408,7 @@ fn create_index_with_typo_and_prefix() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Exactness]);
|
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -442,7 +446,11 @@ fn create_index_all_equal_except_proximity_between_ignored_terms() -> TempIndex
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Exactness,
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Proximity,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -698,7 +706,7 @@ fn test_exactness_after_words() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Exactness]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -747,7 +755,7 @@ fn test_words_after_exactness() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Exactness, Criterion::Words]);
|
s.set_ranking_rules(vec![RankingRule::Exactness, RankingRule::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -796,7 +804,11 @@ fn test_proximity_after_exactness() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Exactness,
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Proximity,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -834,7 +846,11 @@ fn test_proximity_after_exactness() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Exactness,
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Proximity,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -868,7 +884,11 @@ fn test_exactness_followed_by_typo_prefer_no_typo_prefix() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Typo]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Exactness,
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Typo,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -904,7 +924,11 @@ fn test_typo_followed_by_exactness() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Typo, Criterion::Exactness]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Typo,
|
||||||
|
RankingRule::Exactness,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use maplit::hashset;
|
|||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::score_details::ScoreDetails;
|
use crate::score_details::ScoreDetails;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{AscDesc, Criterion, GeoSortStrategy, Member, Search, SearchResult};
|
use crate::{AscDesc, GeoSortStrategy, Member, RankingRule, Search, SearchResult};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -18,7 +18,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_sortable_fields(hashset! { S("_geo") });
|
s.set_sortable_fields(hashset! { S("_geo") });
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Sort]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
index
|
index
|
||||||
|
@ -6,10 +6,10 @@ use maplit::{btreemap, hashset};
|
|||||||
|
|
||||||
use crate::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
use crate::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
||||||
use crate::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
use crate::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
||||||
use crate::{db_snap, Criterion, Index, Object};
|
use crate::{db_snap, Index, Object, RankingRule};
|
||||||
pub const CONTENT: &str = include_str!("../../../../tests/assets/test_set.ndjson");
|
pub const CONTENT: &str = include_str!("../../../../tests/assets/test_set.ndjson");
|
||||||
|
|
||||||
pub fn setup_search_index_with_criteria(criteria: &[Criterion]) -> Index {
|
pub fn setup_search_index_with_criteria(criteria: &[RankingRule]) -> Index {
|
||||||
let path = tempfile::tempdir().unwrap();
|
let path = tempfile::tempdir().unwrap();
|
||||||
let mut options = EnvOpenOptions::new();
|
let mut options = EnvOpenOptions::new();
|
||||||
options.map_size(10 * 1024 * 1024); // 10 MB
|
options.map_size(10 * 1024 * 1024); // 10 MB
|
||||||
@ -20,7 +20,7 @@ pub fn setup_search_index_with_criteria(criteria: &[Criterion]) -> Index {
|
|||||||
|
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
||||||
|
|
||||||
builder.set_criteria(criteria.to_vec());
|
builder.set_ranking_rules(criteria.to_vec());
|
||||||
builder.set_filterable_fields(hashset! {
|
builder.set_filterable_fields(hashset! {
|
||||||
S("tag"),
|
S("tag"),
|
||||||
S("asc_desc_rank"),
|
S("asc_desc_rank"),
|
||||||
@ -70,6 +70,6 @@ pub fn setup_search_index_with_criteria(criteria: &[Criterion]) -> Index {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn snapshot_integration_dataset() {
|
fn snapshot_integration_dataset() {
|
||||||
let index = setup_search_index_with_criteria(&[Criterion::Attribute]);
|
let index = setup_search_index_with_criteria(&[RankingRule::Attribute]);
|
||||||
db_snap!(index, word_position_docids, @"3c9347a767bceef3beb31465f1e5f3ae");
|
db_snap!(index, word_position_docids, @"3c9347a767bceef3beb31465f1e5f3ae");
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ This module tests the following properties:
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -28,7 +28,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Words]);
|
s.set_ranking_rules(vec![RankingRule::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_simple_index() -> TempIndex {
|
fn create_simple_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -28,7 +28,7 @@ fn create_simple_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ fn create_edge_cases_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ implemented.
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -17,7 +17,11 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Proximity, Criterion::Typo]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Proximity,
|
||||||
|
RankingRule::Typo,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ use maplit::hashset;
|
|||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{
|
use crate::{
|
||||||
score_details, AscDesc, Criterion, Member, Search, SearchResult, TermsMatchingStrategy,
|
score_details, AscDesc, Member, RankingRule, Search, SearchResult, TermsMatchingStrategy,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
@ -28,7 +28,7 @@ fn create_index() -> TempIndex {
|
|||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_sortable_fields(hashset! { S("rank"), S("vague"), S("letter") });
|
s.set_sortable_fields(hashset! { S("rank"), S("vague"), S("letter") });
|
||||||
s.set_criteria(vec![Criterion::Sort]);
|
s.set_ranking_rules(vec![RankingRule::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -331,7 +331,7 @@ fn test_redacted() {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_displayed_fields(vec!["text".to_owned(), "vague".to_owned()]);
|
s.set_displayed_fields(vec!["text".to_owned(), "vague".to_owned()]);
|
||||||
s.set_sortable_fields(hashset! { S("rank"), S("vague"), S("letter") });
|
s.set_sortable_fields(hashset! { S("rank"), S("vague"), S("letter") });
|
||||||
s.set_criteria(vec![Criterion::Sort]);
|
s.set_ranking_rules(vec![RankingRule::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -31,7 +31,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Words]);
|
s.set_ranking_rules(vec![RankingRule::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -457,7 +457,7 @@ fn test_typo_ranking_rule_not_preceded_by_words_ranking_rule() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Typo]);
|
s.set_ranking_rules(vec![RankingRule::Typo]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -495,7 +495,7 @@ fn test_typo_ranking_rule_not_preceded_by_words_ranking_rule() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Typo]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Typo]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -540,7 +540,7 @@ fn test_typo_bucketing() {
|
|||||||
drop(txn);
|
drop(txn);
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Typo]);
|
s.set_ranking_rules(vec![RankingRule::Typo]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let txn = index.read_txn().unwrap();
|
let txn = index.read_txn().unwrap();
|
||||||
@ -589,7 +589,7 @@ fn test_typo_synonyms() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Typo]);
|
s.set_ranking_rules(vec![RankingRule::Typo]);
|
||||||
|
|
||||||
let mut synonyms = BTreeMap::new();
|
let mut synonyms = BTreeMap::new();
|
||||||
synonyms.insert("lackadaisical".to_owned(), vec!["lazy".to_owned()]);
|
synonyms.insert("lackadaisical".to_owned(), vec!["lazy".to_owned()]);
|
||||||
|
@ -17,7 +17,7 @@ because the typo ranking rule before it only used the derivation `beautiful`.
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -26,7 +26,11 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Typo, Criterion::Proximity]);
|
s.set_ranking_rules(vec![
|
||||||
|
RankingRule::Words,
|
||||||
|
RankingRule::Typo,
|
||||||
|
RankingRule::Proximity,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ account by the proximity ranking rule.
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@ -23,7 +23,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_criteria(vec![Criterion::Words]);
|
s.set_ranking_rules(vec![RankingRule::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ fn test_words_proximity_tms_last_simple() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ fn test_words_proximity_tms_last_phrase() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -416,7 +416,7 @@ fn test_words_tms_all() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ use time::OffsetDateTime;
|
|||||||
|
|
||||||
use super::index_documents::{IndexDocumentsConfig, Transform};
|
use super::index_documents::{IndexDocumentsConfig, Transform};
|
||||||
use super::IndexerConfig;
|
use super::IndexerConfig;
|
||||||
use crate::criterion::Criterion;
|
|
||||||
use crate::error::UserError;
|
use crate::error::UserError;
|
||||||
use crate::index::{DEFAULT_MIN_WORD_LEN_ONE_TYPO, DEFAULT_MIN_WORD_LEN_TWO_TYPOS};
|
use crate::index::{DEFAULT_MIN_WORD_LEN_ONE_TYPO, DEFAULT_MIN_WORD_LEN_TWO_TYPOS};
|
||||||
|
use crate::ranking_rule::RankingRule;
|
||||||
use crate::update::index_documents::IndexDocumentsMethod;
|
use crate::update::index_documents::IndexDocumentsMethod;
|
||||||
use crate::update::{IndexDocuments, UpdateIndexingStep};
|
use crate::update::{IndexDocuments, UpdateIndexingStep};
|
||||||
use crate::{FieldsIdsMap, Index, OrderBy, Result};
|
use crate::{FieldsIdsMap, Index, OrderBy, Result};
|
||||||
@ -110,7 +110,7 @@ pub struct Settings<'a, 't, 'u, 'i> {
|
|||||||
displayed_fields: Setting<Vec<String>>,
|
displayed_fields: Setting<Vec<String>>,
|
||||||
filterable_fields: Setting<HashSet<String>>,
|
filterable_fields: Setting<HashSet<String>>,
|
||||||
sortable_fields: Setting<HashSet<String>>,
|
sortable_fields: Setting<HashSet<String>>,
|
||||||
criteria: Setting<Vec<Criterion>>,
|
ranking_rules: Setting<Vec<RankingRule>>,
|
||||||
stop_words: Setting<BTreeSet<String>>,
|
stop_words: Setting<BTreeSet<String>>,
|
||||||
non_separator_tokens: Setting<BTreeSet<String>>,
|
non_separator_tokens: Setting<BTreeSet<String>>,
|
||||||
separator_tokens: Setting<BTreeSet<String>>,
|
separator_tokens: Setting<BTreeSet<String>>,
|
||||||
@ -142,7 +142,7 @@ impl<'a, 't, 'u, 'i> Settings<'a, 't, 'u, 'i> {
|
|||||||
displayed_fields: Setting::NotSet,
|
displayed_fields: Setting::NotSet,
|
||||||
filterable_fields: Setting::NotSet,
|
filterable_fields: Setting::NotSet,
|
||||||
sortable_fields: Setting::NotSet,
|
sortable_fields: Setting::NotSet,
|
||||||
criteria: Setting::NotSet,
|
ranking_rules: Setting::NotSet,
|
||||||
stop_words: Setting::NotSet,
|
stop_words: Setting::NotSet,
|
||||||
non_separator_tokens: Setting::NotSet,
|
non_separator_tokens: Setting::NotSet,
|
||||||
separator_tokens: Setting::NotSet,
|
separator_tokens: Setting::NotSet,
|
||||||
@ -194,12 +194,12 @@ impl<'a, 't, 'u, 'i> Settings<'a, 't, 'u, 'i> {
|
|||||||
self.sortable_fields = Setting::Reset;
|
self.sortable_fields = Setting::Reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_criteria(&mut self) {
|
pub fn reset_ranking_rules(&mut self) {
|
||||||
self.criteria = Setting::Reset;
|
self.ranking_rules = Setting::Reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_criteria(&mut self, criteria: Vec<Criterion>) {
|
pub fn set_ranking_rules(&mut self, ranking_rules: Vec<RankingRule>) {
|
||||||
self.criteria = Setting::Set(criteria);
|
self.ranking_rules = Setting::Set(ranking_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_stop_words(&mut self) {
|
pub fn reset_stop_words(&mut self) {
|
||||||
@ -696,7 +696,7 @@ impl<'a, 't, 'u, 'i> Settings<'a, 't, 'u, 'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_criteria(&mut self) -> Result<()> {
|
fn update_criteria(&mut self) -> Result<()> {
|
||||||
match &self.criteria {
|
match &self.ranking_rules {
|
||||||
Setting::Set(criteria) => {
|
Setting::Set(criteria) => {
|
||||||
self.index.put_criteria(self.wtxn, criteria)?;
|
self.index.put_criteria(self.wtxn, criteria)?;
|
||||||
}
|
}
|
||||||
@ -924,7 +924,7 @@ mod tests {
|
|||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::update::{ClearDocuments, DeleteDocuments};
|
use crate::update::{ClearDocuments, DeleteDocuments};
|
||||||
use crate::{Criterion, Filter, SearchResult};
|
use crate::{Filter, RankingRule, SearchResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_and_reset_searchable_fields() {
|
fn set_and_reset_searchable_fields() {
|
||||||
@ -1167,7 +1167,7 @@ mod tests {
|
|||||||
index
|
index
|
||||||
.update_settings(|settings| {
|
.update_settings(|settings| {
|
||||||
settings.set_displayed_fields(vec![S("name")]);
|
settings.set_displayed_fields(vec![S("name")]);
|
||||||
settings.set_criteria(vec![Criterion::Asc("age".to_owned())]);
|
settings.set_ranking_rules(vec![RankingRule::Asc("age".to_owned())]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -1473,7 +1473,7 @@ mod tests {
|
|||||||
.update_settings(|settings| {
|
.update_settings(|settings| {
|
||||||
settings.set_displayed_fields(vec!["hello".to_string()]);
|
settings.set_displayed_fields(vec!["hello".to_string()]);
|
||||||
settings.set_filterable_fields(hashset! { S("age"), S("toto") });
|
settings.set_filterable_fields(hashset! { S("age"), S("toto") });
|
||||||
settings.set_criteria(vec![Criterion::Asc(S("toto"))]);
|
settings.set_ranking_rules(vec![RankingRule::Asc(S("toto"))]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -1482,7 +1482,7 @@ mod tests {
|
|||||||
assert_eq!(&["hello"][..], index.displayed_fields(&rtxn).unwrap().unwrap());
|
assert_eq!(&["hello"][..], index.displayed_fields(&rtxn).unwrap().unwrap());
|
||||||
// since no documents have been pushed the primary key is still unset
|
// since no documents have been pushed the primary key is still unset
|
||||||
assert!(index.primary_key(&rtxn).unwrap().is_none());
|
assert!(index.primary_key(&rtxn).unwrap().is_none());
|
||||||
assert_eq!(vec![Criterion::Asc("toto".to_string())], index.criteria(&rtxn).unwrap());
|
assert_eq!(vec![RankingRule::Asc("toto".to_string())], index.criteria(&rtxn).unwrap());
|
||||||
drop(rtxn);
|
drop(rtxn);
|
||||||
|
|
||||||
// We set toto and age as searchable to force reordering of the fields
|
// We set toto and age as searchable to force reordering of the fields
|
||||||
@ -1495,7 +1495,7 @@ mod tests {
|
|||||||
let rtxn = index.read_txn().unwrap();
|
let rtxn = index.read_txn().unwrap();
|
||||||
assert_eq!(&["hello"][..], index.displayed_fields(&rtxn).unwrap().unwrap());
|
assert_eq!(&["hello"][..], index.displayed_fields(&rtxn).unwrap().unwrap());
|
||||||
assert!(index.primary_key(&rtxn).unwrap().is_none());
|
assert!(index.primary_key(&rtxn).unwrap().is_none());
|
||||||
assert_eq!(vec![Criterion::Asc("toto".to_string())], index.criteria(&rtxn).unwrap());
|
assert_eq!(vec![RankingRule::Asc("toto".to_string())], index.criteria(&rtxn).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1507,7 +1507,7 @@ mod tests {
|
|||||||
.update_settings(|settings| {
|
.update_settings(|settings| {
|
||||||
settings.set_displayed_fields(vec!["hello".to_string()]);
|
settings.set_displayed_fields(vec!["hello".to_string()]);
|
||||||
// It is only Asc(toto), there is a facet database but it is denied to filter with toto.
|
// It is only Asc(toto), there is a facet database but it is denied to filter with toto.
|
||||||
settings.set_criteria(vec![Criterion::Asc(S("toto"))]);
|
settings.set_ranking_rules(vec![RankingRule::Asc(S("toto"))]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -1715,7 +1715,7 @@ mod tests {
|
|||||||
displayed_fields,
|
displayed_fields,
|
||||||
filterable_fields,
|
filterable_fields,
|
||||||
sortable_fields,
|
sortable_fields,
|
||||||
criteria,
|
ranking_rules: criteria,
|
||||||
stop_words,
|
stop_words,
|
||||||
non_separator_tokens,
|
non_separator_tokens,
|
||||||
separator_tokens,
|
separator_tokens,
|
||||||
|
@ -2,8 +2,8 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
use big_s::S;
|
use big_s::S;
|
||||||
use milli::update::Settings;
|
use milli::update::Settings;
|
||||||
use milli::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
use milli::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
use Criterion::*;
|
use RankingRule::*;
|
||||||
|
|
||||||
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use either::{Either, Left, Right};
|
use either::{Either, Left, Right};
|
||||||
use milli::{Criterion, Filter, Search, SearchResult, TermsMatchingStrategy};
|
use milli::{Filter, RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
use Criterion::*;
|
use RankingRule::*;
|
||||||
|
|
||||||
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use heed::EnvOpenOptions;
|
|||||||
use maplit::{btreemap, hashset};
|
use maplit::{btreemap, hashset};
|
||||||
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
||||||
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
||||||
use milli::{AscDesc, Criterion, DocumentId, Index, Member, Object, TermsMatchingStrategy};
|
use milli::{AscDesc, DocumentId, Index, Member, Object, RankingRule, TermsMatchingStrategy};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use slice_group_by::GroupBy;
|
use slice_group_by::GroupBy;
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ pub const EXTERNAL_DOCUMENTS_IDS: &[&str; 17] =
|
|||||||
|
|
||||||
pub const CONTENT: &str = include_str!("../assets/test_set.ndjson");
|
pub const CONTENT: &str = include_str!("../assets/test_set.ndjson");
|
||||||
|
|
||||||
pub fn setup_search_index_with_criteria(criteria: &[Criterion]) -> Index {
|
pub fn setup_search_index_with_criteria(criteria: &[RankingRule]) -> Index {
|
||||||
let path = tempfile::tempdir().unwrap();
|
let path = tempfile::tempdir().unwrap();
|
||||||
let mut options = EnvOpenOptions::new();
|
let mut options = EnvOpenOptions::new();
|
||||||
options.map_size(10 * 1024 * 1024); // 10 MB
|
options.map_size(10 * 1024 * 1024); // 10 MB
|
||||||
@ -38,7 +38,7 @@ pub fn setup_search_index_with_criteria(criteria: &[Criterion]) -> Index {
|
|||||||
|
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
||||||
|
|
||||||
builder.set_criteria(criteria.to_vec());
|
builder.set_ranking_rules(criteria.to_vec());
|
||||||
builder.set_filterable_fields(hashset! {
|
builder.set_filterable_fields(hashset! {
|
||||||
S("tag"),
|
S("tag"),
|
||||||
S("asc_desc_rank"),
|
S("asc_desc_rank"),
|
||||||
@ -95,7 +95,7 @@ pub fn internal_to_external_ids(index: &Index, internal_ids: &[DocumentId]) -> V
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn expected_order(
|
pub fn expected_order(
|
||||||
criteria: &[Criterion],
|
criteria: &[RankingRule],
|
||||||
optional_words: TermsMatchingStrategy,
|
optional_words: TermsMatchingStrategy,
|
||||||
sort_by: &[AscDesc],
|
sort_by: &[AscDesc],
|
||||||
) -> Vec<TestDocument> {
|
) -> Vec<TestDocument> {
|
||||||
@ -107,47 +107,56 @@ pub fn expected_order(
|
|||||||
let mut new_groups = Vec::new();
|
let mut new_groups = Vec::new();
|
||||||
for group in groups.iter_mut() {
|
for group in groups.iter_mut() {
|
||||||
match criterion {
|
match criterion {
|
||||||
Criterion::Attribute => {
|
RankingRule::Attribute => {
|
||||||
group.sort_by_key(|d| d.attribute_rank);
|
group.sort_by_key(|d| d.attribute_rank);
|
||||||
new_groups
|
new_groups
|
||||||
.extend(group.linear_group_by_key(|d| d.attribute_rank).map(Vec::from));
|
.extend(group.linear_group_by_key(|d| d.attribute_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
Criterion::Exactness => {
|
RankingRule::Exactness => {
|
||||||
group.sort_by_key(|d| d.exact_rank);
|
group.sort_by_key(|d| d.exact_rank);
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.exact_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.exact_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
Criterion::Proximity => {
|
RankingRule::Proximity => {
|
||||||
group.sort_by_key(|d| d.proximity_rank);
|
group.sort_by_key(|d| d.proximity_rank);
|
||||||
new_groups
|
new_groups
|
||||||
.extend(group.linear_group_by_key(|d| d.proximity_rank).map(Vec::from));
|
.extend(group.linear_group_by_key(|d| d.proximity_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
Criterion::Sort if sort_by == [AscDesc::Asc(Member::Field(S("tag")))] => {
|
RankingRule::Sort if sort_by == [AscDesc::Asc(Member::Field(S("tag")))] => {
|
||||||
group.sort_by_key(|d| d.sort_by_rank);
|
group.sort_by_key(|d| d.sort_by_rank);
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.sort_by_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.sort_by_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
Criterion::Sort if sort_by == [AscDesc::Desc(Member::Field(S("tag")))] => {
|
RankingRule::Sort if sort_by == [AscDesc::Desc(Member::Field(S("tag")))] => {
|
||||||
group.sort_by_key(|d| Reverse(d.sort_by_rank));
|
group.sort_by_key(|d| Reverse(d.sort_by_rank));
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.sort_by_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.sort_by_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
Criterion::Typo => {
|
RankingRule::Typo => {
|
||||||
group.sort_by_key(|d| d.typo_rank);
|
group.sort_by_key(|d| d.typo_rank);
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.typo_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.typo_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
Criterion::Words => {
|
RankingRule::Words => {
|
||||||
group.sort_by_key(|d| d.word_rank);
|
group.sort_by_key(|d| d.word_rank);
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.word_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.word_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
Criterion::Asc(field_name) if field_name == "asc_desc_rank" => {
|
RankingRule::Asc(field_name) if field_name == "asc_desc_rank" => {
|
||||||
group.sort_by_key(|d| d.asc_desc_rank);
|
group.sort_by_key(|d| d.asc_desc_rank);
|
||||||
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));
|
||||||
}
|
}
|
||||||
Criterion::Desc(field_name) if field_name == "asc_desc_rank" => {
|
RankingRule::Desc(field_name) if field_name == "asc_desc_rank" => {
|
||||||
group.sort_by_key(|d| Reverse(d.asc_desc_rank));
|
group.sort_by_key(|d| Reverse(d.asc_desc_rank));
|
||||||
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));
|
||||||
}
|
}
|
||||||
Criterion::Asc(_) | Criterion::Desc(_) | Criterion::Sort => {
|
RankingRule::FilterBoosting(filter) => {
|
||||||
|
// 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 });
|
||||||
|
new_groups.extend(
|
||||||
|
group
|
||||||
|
.linear_group_by_key(|d| execute_filter(filter, d).is_some())
|
||||||
|
.map(Vec::from),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RankingRule::Asc(_) | RankingRule::Desc(_) | RankingRule::Sort => {
|
||||||
new_groups.push(group.clone())
|
new_groups.push(group.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use milli::update::{IndexerConfig, Settings};
|
use milli::update::{IndexerConfig, Settings};
|
||||||
use milli::{Criterion, Index, Search, TermsMatchingStrategy};
|
use milli::{Index, RankingRule, Search, TermsMatchingStrategy};
|
||||||
|
|
||||||
use crate::search::Criterion::{Attribute, Exactness, Proximity};
|
use crate::search::RankingRule::{Attribute, Exactness, Proximity};
|
||||||
|
|
||||||
fn set_stop_words(index: &Index, stop_words: &[&str]) {
|
fn set_stop_words(index: &Index, stop_words: &[&str]) {
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
let mut wtxn = index.write_txn().unwrap();
|
||||||
@ -14,7 +14,7 @@ fn set_stop_words(index: &Index, stop_words: &[&str]) {
|
|||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_phrase_search_with_stop_words_given_criteria(criteria: &[Criterion]) {
|
fn test_phrase_search_with_stop_words_given_criteria(criteria: &[RankingRule]) {
|
||||||
let index = super::setup_search_index_with_criteria(criteria);
|
let index = super::setup_search_index_with_criteria(criteria);
|
||||||
|
|
||||||
// Add stop_words
|
// Add stop_words
|
||||||
|
@ -7,9 +7,9 @@ use itertools::Itertools;
|
|||||||
use maplit::hashset;
|
use maplit::hashset;
|
||||||
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
||||||
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
||||||
use milli::{AscDesc, Criterion, Index, Member, Search, SearchResult, TermsMatchingStrategy};
|
use milli::{AscDesc, Index, Member, RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use Criterion::*;
|
use RankingRule::*;
|
||||||
|
|
||||||
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ test_criterion!(
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn criteria_mixup() {
|
fn criteria_mixup() {
|
||||||
use Criterion::*;
|
use RankingRule::*;
|
||||||
let index = search::setup_search_index_with_criteria(&[
|
let index = search::setup_search_index_with_criteria(&[
|
||||||
Words,
|
Words,
|
||||||
Attribute,
|
Attribute,
|
||||||
@ -233,7 +233,7 @@ fn criteria_mixup() {
|
|||||||
//update criteria
|
//update criteria
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
let mut wtxn = index.write_txn().unwrap();
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
||||||
builder.set_criteria(criteria.clone());
|
builder.set_ranking_rules(criteria.clone());
|
||||||
builder.execute(|_| (), || false).unwrap();
|
builder.execute(|_| (), || false).unwrap();
|
||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ fn criteria_ascdesc() {
|
|||||||
|
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
let mut wtxn = index.write_txn().unwrap();
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
||||||
builder.set_criteria(vec![criterion.clone()]);
|
builder.set_ranking_rules(vec![criterion.clone()]);
|
||||||
builder.execute(|_| (), || false).unwrap();
|
builder.execute(|_| (), || false).unwrap();
|
||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use big_s::S;
|
use big_s::S;
|
||||||
use milli::Criterion::{Attribute, Exactness, Proximity, Typo, Words};
|
use milli::RankingRule::{Attribute, Exactness, Proximity, Typo, Words};
|
||||||
use milli::{AscDesc, Error, Member, Search, TermsMatchingStrategy, UserError};
|
use milli::{AscDesc, Error, Member, Search, TermsMatchingStrategy, UserError};
|
||||||
|
|
||||||
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
||||||
|
@ -2,10 +2,10 @@ use std::collections::BTreeSet;
|
|||||||
|
|
||||||
use heed::EnvOpenOptions;
|
use heed::EnvOpenOptions;
|
||||||
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
||||||
use milli::{Criterion, Index, Search, TermsMatchingStrategy};
|
use milli::{Index, RankingRule, Search, TermsMatchingStrategy};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use Criterion::*;
|
use RankingRule::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_typo_tolerance_one_typo() {
|
fn test_typo_tolerance_one_typo() {
|
||||||
|
Reference in New Issue
Block a user