Expose a sortFacetValuesBy parameter to the user

This commit is contained in:
Kerollmops 2023-05-29 15:32:09 +02:00 committed by Clément Renault
parent 80bbd4b6f3
commit 34b2e98fe9
No known key found for this signature in database
GPG Key ID: 92ADA4E935E71FA4
6 changed files with 45 additions and 13 deletions

View File

@ -16,9 +16,9 @@ use crate::extractors::authentication::policies::*;
use crate::extractors::authentication::GuardedData; use crate::extractors::authentication::GuardedData;
use crate::extractors::sequential_extractor::SeqHandler; use crate::extractors::sequential_extractor::SeqHandler;
use crate::search::{ use crate::search::{
add_search_rules, perform_search, MatchingStrategy, SearchQuery, DEFAULT_CROP_LENGTH, add_search_rules, perform_search, FacetValuesSort, MatchingStrategy, SearchQuery,
DEFAULT_CROP_MARKER, DEFAULT_HIGHLIGHT_POST_TAG, DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER, DEFAULT_HIGHLIGHT_POST_TAG,
DEFAULT_SEARCH_LIMIT, DEFAULT_SEARCH_OFFSET, DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT, DEFAULT_SEARCH_OFFSET,
}; };
pub fn configure(cfg: &mut web::ServiceConfig) { pub fn configure(cfg: &mut web::ServiceConfig) {
@ -64,6 +64,8 @@ pub struct SearchQueryGet {
show_ranking_score_details: Param<bool>, show_ranking_score_details: Param<bool>,
#[deserr(default, error = DeserrQueryParamError<InvalidSearchFacets>)] #[deserr(default, error = DeserrQueryParamError<InvalidSearchFacets>)]
facets: Option<CS<String>>, facets: Option<CS<String>>,
#[deserr(default, error = DeserrQueryParamError<InvalidSearchFacets>)]
sort_facet_values_by: Option<FacetValuesSort>,
#[deserr( default = DEFAULT_HIGHLIGHT_PRE_TAG(), error = DeserrQueryParamError<InvalidSearchHighlightPreTag>)] #[deserr( default = DEFAULT_HIGHLIGHT_PRE_TAG(), error = DeserrQueryParamError<InvalidSearchHighlightPreTag>)]
highlight_pre_tag: String, highlight_pre_tag: String,
#[deserr( default = DEFAULT_HIGHLIGHT_POST_TAG(), error = DeserrQueryParamError<InvalidSearchHighlightPostTag>)] #[deserr( default = DEFAULT_HIGHLIGHT_POST_TAG(), error = DeserrQueryParamError<InvalidSearchHighlightPostTag>)]
@ -103,6 +105,7 @@ impl From<SearchQueryGet> for SearchQuery {
show_ranking_score: other.show_ranking_score.0, show_ranking_score: other.show_ranking_score.0,
show_ranking_score_details: other.show_ranking_score_details.0, show_ranking_score_details: other.show_ranking_score_details.0,
facets: other.facets.map(|o| o.into_iter().collect()), facets: other.facets.map(|o| o.into_iter().collect()),
sort_facet_values_by: other.sort_facet_values_by,
highlight_pre_tag: other.highlight_pre_tag, highlight_pre_tag: other.highlight_pre_tag,
highlight_post_tag: other.highlight_post_tag, highlight_post_tag: other.highlight_post_tag,
crop_marker: other.crop_marker, crop_marker: other.crop_marker,

View File

@ -14,7 +14,7 @@ use meilisearch_types::heed::RoTxn;
use meilisearch_types::index_uid::IndexUid; use meilisearch_types::index_uid::IndexUid;
use meilisearch_types::milli::score_details::{ScoreDetails, ScoringStrategy}; use meilisearch_types::milli::score_details::{ScoreDetails, ScoringStrategy};
use meilisearch_types::milli::{ use meilisearch_types::milli::{
dot_product_similarity, FacetValueHit, InternalError, SearchForFacetValues, dot_product_similarity, FacetValueHit, OrderBy, InternalError, SearchForFacetValues,
}; };
use meilisearch_types::settings::DEFAULT_PAGINATION_MAX_TOTAL_HITS; use meilisearch_types::settings::DEFAULT_PAGINATION_MAX_TOTAL_HITS;
use meilisearch_types::{milli, Document}; use meilisearch_types::{milli, Document};
@ -74,6 +74,8 @@ pub struct SearchQuery {
pub sort: Option<Vec<String>>, pub sort: Option<Vec<String>>,
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)] #[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
pub facets: Option<Vec<String>>, pub facets: Option<Vec<String>>,
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)] // TODO
pub sort_facet_values_by: Option<FacetValuesSort>,
#[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())] #[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
pub highlight_pre_tag: String, pub highlight_pre_tag: String,
#[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())] #[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
@ -133,6 +135,8 @@ pub struct SearchQueryWithIndex {
pub sort: Option<Vec<String>>, pub sort: Option<Vec<String>>,
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)] #[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
pub facets: Option<Vec<String>>, pub facets: Option<Vec<String>>,
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)] // TODO
pub sort_facet_values_by: Option<FacetValuesSort>,
#[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())] #[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
pub highlight_pre_tag: String, pub highlight_pre_tag: String,
#[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())] #[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
@ -165,6 +169,7 @@ impl SearchQueryWithIndex {
filter, filter,
sort, sort,
facets, facets,
sort_facet_values_by,
highlight_pre_tag, highlight_pre_tag,
highlight_post_tag, highlight_post_tag,
crop_marker, crop_marker,
@ -190,6 +195,7 @@ impl SearchQueryWithIndex {
filter, filter,
sort, sort,
facets, facets,
sort_facet_values_by,
highlight_pre_tag, highlight_pre_tag,
highlight_post_tag, highlight_post_tag,
crop_marker, crop_marker,
@ -226,6 +232,26 @@ impl From<MatchingStrategy> for TermsMatchingStrategy {
} }
} }
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserr)]
#[deserr(rename_all = camelCase)]
pub enum FacetValuesSort {
/// Facet values are sorted by decreasing count.
/// The count is the number of records containing this facet value in the results of the query.
#[default]
Alpha,
/// Facet values are sorted in alphabetical order, ascending from A to Z.
Count,
}
impl Into<OrderBy> for FacetValuesSort {
fn into(self) -> OrderBy {
match self {
FacetValuesSort::Alpha => OrderBy::Lexicographic,
FacetValuesSort::Count => OrderBy::Count,
}
}
}
#[derive(Debug, Clone, Serialize, PartialEq)] #[derive(Debug, Clone, Serialize, PartialEq)]
pub struct SearchHit { pub struct SearchHit {
#[serde(flatten)] #[serde(flatten)]
@ -557,7 +583,10 @@ pub fn perform_search(
if fields.iter().all(|f| f != "*") { if fields.iter().all(|f| f != "*") {
facet_distribution.facets(fields); facet_distribution.facets(fields);
} }
let distribution = facet_distribution.candidates(candidates).execute()?; let distribution = facet_distribution
.candidates(candidates)
.order_by(query.sort_facet_values_by.map_or_else(Default::default, Into::into))
.execute()?;
let stats = facet_distribution.compute_stats()?; let stats = facet_distribution.compute_stats()?;
(Some(distribution), Some(stats)) (Some(distribution), Some(stats))
} }

View File

@ -58,7 +58,7 @@ pub use self::heed_codec::{
pub use self::index::Index; pub use self::index::Index;
pub use self::search::{ pub use self::search::{
FacetDistribution, FacetValueHit, Filter, FormatOptions, MatchBounds, MatcherBuilder, FacetDistribution, FacetValueHit, Filter, FormatOptions, MatchBounds, MatcherBuilder,
MatchingWords, Search, SearchForFacetValues, SearchResult, TermsMatchingStrategy, MatchingWords, OrderBy, Search, SearchForFacetValues, SearchResult, TermsMatchingStrategy,
DEFAULT_VALUES_PER_FACET, DEFAULT_VALUES_PER_FACET,
}; };

View File

@ -12,11 +12,10 @@ use crate::heed_codec::facet::{
FacetGroupKeyCodec, FieldDocIdFacetF64Codec, FieldDocIdFacetStringCodec, OrderedF64Codec, FacetGroupKeyCodec, FieldDocIdFacetF64Codec, FieldDocIdFacetStringCodec, OrderedF64Codec,
}; };
use crate::heed_codec::{ByteSliceRefCodec, StrRefCodec}; use crate::heed_codec::{ByteSliceRefCodec, StrRefCodec};
use crate::search::facet::facet_distribution_iter; use crate::search::facet::facet_distribution_iter::{
use crate::{FieldId, Index, Result};
use facet_distribution_iter::{
count_iterate_over_facet_distribution, lexicographically_iterate_over_facet_distribution, count_iterate_over_facet_distribution, lexicographically_iterate_over_facet_distribution,
}; };
use crate::{FieldId, Index, Result};
/// The default number of values by facets that will /// The default number of values by facets that will
/// be fetched from the key-value store. /// be fetched from the key-value store.
@ -27,9 +26,10 @@ pub const DEFAULT_VALUES_PER_FACET: usize = 100;
const CANDIDATES_THRESHOLD: u64 = 3000; const CANDIDATES_THRESHOLD: u64 = 3000;
/// How should we fetch the facets? /// How should we fetch the facets?
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum OrderBy { pub enum OrderBy {
/// By lexicographic order... /// By lexicographic order...
#[default]
Lexicographic, Lexicographic,
/// Or by number of docids in common? /// Or by number of docids in common?
Count, Count,
@ -50,7 +50,7 @@ impl<'a> FacetDistribution<'a> {
facets: None, facets: None,
candidates: None, candidates: None,
max_values_per_facet: DEFAULT_VALUES_PER_FACET, max_values_per_facet: DEFAULT_VALUES_PER_FACET,
order_by: OrderBy::Count, order_by: OrderBy::default(),
rtxn, rtxn,
index, index,
} }

View File

@ -4,7 +4,7 @@ use heed::types::{ByteSlice, DecodeIgnore};
use heed::{BytesDecode, RoTxn}; use heed::{BytesDecode, RoTxn};
use roaring::RoaringBitmap; use roaring::RoaringBitmap;
pub use self::facet_distribution::{FacetDistribution, DEFAULT_VALUES_PER_FACET}; pub use self::facet_distribution::{FacetDistribution, OrderBy, DEFAULT_VALUES_PER_FACET};
pub use self::filter::{BadGeoError, Filter}; pub use self::filter::{BadGeoError, Filter};
use crate::heed_codec::facet::{FacetGroupKeyCodec, FacetGroupValueCodec, OrderedF64Codec}; use crate::heed_codec::facet::{FacetGroupKeyCodec, FacetGroupValueCodec, OrderedF64Codec};
use crate::heed_codec::ByteSliceRefCodec; use crate::heed_codec::ByteSliceRefCodec;

View File

@ -7,7 +7,7 @@ use log::error;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use roaring::bitmap::RoaringBitmap; use roaring::bitmap::RoaringBitmap;
pub use self::facet::{FacetDistribution, Filter, DEFAULT_VALUES_PER_FACET}; pub use self::facet::{FacetDistribution, Filter, OrderBy, DEFAULT_VALUES_PER_FACET};
pub use self::new::matches::{FormatOptions, MatchBounds, Matcher, MatcherBuilder, MatchingWords}; pub use self::new::matches::{FormatOptions, MatchBounds, Matcher, MatcherBuilder, MatchingWords};
use self::new::PartialSearchResult; use self::new::PartialSearchResult;
use crate::error::UserError; use crate::error::UserError;