mirror of
https://github.com/meilisearch/meilisearch.git
synced 2024-11-26 20:15:07 +08:00
Return the correct response JSON object from the facet-search route
This commit is contained in:
parent
893592c5e9
commit
93f30e65a9
@ -2,16 +2,13 @@ use std::collections::{BTreeSet, HashSet};
|
|||||||
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_web::{web, HttpRequest, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use deserr::actix_web::{AwebJson, AwebQueryParameter};
|
use deserr::actix_web::AwebJson;
|
||||||
use index_scheduler::IndexScheduler;
|
use index_scheduler::IndexScheduler;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use meilisearch_types::deserr::query_params::Param;
|
use meilisearch_types::deserr::DeserrJsonError;
|
||||||
use meilisearch_types::deserr::{DeserrJsonError, DeserrQueryParamError};
|
|
||||||
use meilisearch_types::error::deserr_codes::*;
|
use meilisearch_types::error::deserr_codes::*;
|
||||||
use meilisearch_types::error::ResponseError;
|
use meilisearch_types::error::ResponseError;
|
||||||
use meilisearch_types::index_uid::IndexUid;
|
use meilisearch_types::index_uid::IndexUid;
|
||||||
use meilisearch_types::milli::facet;
|
|
||||||
use meilisearch_types::serde_cs::vec::CS;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::analytics::{Analytics, FacetSearchAggregator};
|
use crate::analytics::{Analytics, FacetSearchAggregator};
|
||||||
@ -27,45 +24,6 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||||||
cfg.service(web::resource("").route(web::post().to(search)));
|
cfg.service(web::resource("").route(web::post().to(search)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Debug, deserr::Deserr)]
|
|
||||||
// #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
|
|
||||||
// pub struct FacetSearchQuery {
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchQ>)]
|
|
||||||
// facetQuery: Option<String>,
|
|
||||||
// #[deserr(default = Param(DEFAULT_SEARCH_OFFSET()), error = DeserrQueryParamError<InvalidSearchOffset>)]
|
|
||||||
// offset: Param<usize>,
|
|
||||||
// #[deserr(default = Param(DEFAULT_SEARCH_LIMIT()), error = DeserrQueryParamError<InvalidSearchLimit>)]
|
|
||||||
// limit: Param<usize>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchPage>)]
|
|
||||||
// page: Option<Param<usize>>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchHitsPerPage>)]
|
|
||||||
// hits_per_page: Option<Param<usize>>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchAttributesToRetrieve>)]
|
|
||||||
// attributes_to_retrieve: Option<CS<String>>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchAttributesToCrop>)]
|
|
||||||
// attributes_to_crop: Option<CS<String>>,
|
|
||||||
// #[deserr(default = Param(DEFAULT_CROP_LENGTH()), error = DeserrQueryParamError<InvalidSearchCropLength>)]
|
|
||||||
// crop_length: Param<usize>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchAttributesToHighlight>)]
|
|
||||||
// attributes_to_highlight: Option<CS<String>>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchFilter>)]
|
|
||||||
// filter: Option<String>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchSort>)]
|
|
||||||
// sort: Option<String>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchShowMatchesPosition>)]
|
|
||||||
// show_matches_position: Param<bool>,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchFacets>)]
|
|
||||||
// facets: Option<CS<String>>,
|
|
||||||
// #[deserr( default = DEFAULT_HIGHLIGHT_PRE_TAG(), error = DeserrQueryParamError<InvalidSearchHighlightPreTag>)]
|
|
||||||
// highlight_pre_tag: String,
|
|
||||||
// #[deserr( default = DEFAULT_HIGHLIGHT_POST_TAG(), error = DeserrQueryParamError<InvalidSearchHighlightPostTag>)]
|
|
||||||
// highlight_post_tag: String,
|
|
||||||
// #[deserr(default = DEFAULT_CROP_MARKER(), error = DeserrQueryParamError<InvalidSearchCropMarker>)]
|
|
||||||
// crop_marker: String,
|
|
||||||
// #[deserr(default, error = DeserrQueryParamError<InvalidSearchMatchingStrategy>)]
|
|
||||||
// matching_strategy: MatchingStrategy,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO improve the error messages
|
// TODO improve the error messages
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, deserr::Deserr)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, deserr::Deserr)]
|
||||||
#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
|
#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
|
||||||
|
@ -3,7 +3,6 @@ use std::collections::{BTreeMap, BTreeSet, HashSet};
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use actix_http::header::q;
|
|
||||||
use deserr::Deserr;
|
use deserr::Deserr;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use index_scheduler::RoFeatures;
|
use index_scheduler::RoFeatures;
|
||||||
@ -15,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, facet, FacetSearchResult, InternalError, SearchForFacetValue,
|
dot_product_similarity, FacetValueHit, InternalError, SearchForFacetValue,
|
||||||
};
|
};
|
||||||
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};
|
||||||
@ -30,7 +29,6 @@ use serde::Serialize;
|
|||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::error::MeilisearchHttpError;
|
use crate::error::MeilisearchHttpError;
|
||||||
use crate::routes::indexes::facet_search::FacetSearchQuery;
|
|
||||||
|
|
||||||
type MatchesPosition = BTreeMap<String, Vec<MatchBounds>>;
|
type MatchesPosition = BTreeMap<String, Vec<MatchBounds>>;
|
||||||
|
|
||||||
@ -283,6 +281,14 @@ pub struct FacetStats {
|
|||||||
pub max: f64,
|
pub max: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct FacetSearchResult {
|
||||||
|
pub hits: Vec<FacetValueHit>,
|
||||||
|
pub query: Option<String>,
|
||||||
|
pub processing_time_ms: u128,
|
||||||
|
}
|
||||||
|
|
||||||
/// Incorporate search rules in search query
|
/// Incorporate search rules in search query
|
||||||
pub fn add_search_rules(query: &mut SearchQuery, rules: IndexSearchRules) {
|
pub fn add_search_rules(query: &mut SearchQuery, rules: IndexSearchRules) {
|
||||||
query.filter = match (query.filter.take(), rules.filter) {
|
query.filter = match (query.filter.take(), rules.filter) {
|
||||||
@ -577,19 +583,25 @@ pub fn perform_search(
|
|||||||
pub fn perform_facet_search(
|
pub fn perform_facet_search(
|
||||||
index: &Index,
|
index: &Index,
|
||||||
search_query: SearchQuery,
|
search_query: SearchQuery,
|
||||||
mut facet_query: Option<String>,
|
facet_query: Option<String>,
|
||||||
facet_name: String,
|
facet_name: String,
|
||||||
) -> Result<Vec<FacetSearchResult>, MeilisearchHttpError> {
|
) -> Result<FacetSearchResult, MeilisearchHttpError> {
|
||||||
let before_search = Instant::now();
|
let before_search = Instant::now();
|
||||||
let rtxn = index.read_txn()?;
|
let rtxn = index.read_txn()?;
|
||||||
|
|
||||||
let (search, _, _, _) = prepare_search(index, &rtxn, &search_query)?;
|
let (search, _, _, _) = prepare_search(index, &rtxn, &search_query)?;
|
||||||
let mut facet_search = SearchForFacetValue::new(facet_name, search);
|
let mut facet_search = SearchForFacetValue::new(facet_name, search);
|
||||||
if let Some(facet_query) = facet_query.take() {
|
if let Some(facet_query) = &facet_query {
|
||||||
facet_search.query(facet_query);
|
facet_search.query(facet_query);
|
||||||
}
|
}
|
||||||
|
|
||||||
facet_search.execute().map_err(Into::into)
|
let hits = facet_search.execute()?;
|
||||||
|
|
||||||
|
Ok(FacetSearchResult {
|
||||||
|
hits,
|
||||||
|
query: facet_query,
|
||||||
|
processing_time_ms: before_search.elapsed().as_millis(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_geo_distance(sorts: &[String], document: &mut Document) {
|
fn insert_geo_distance(sorts: &[String], document: &mut Document) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use fst::Set;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use fst::Set;
|
||||||
use heed::{BytesDecode, BytesEncode};
|
use heed::{BytesDecode, BytesEncode};
|
||||||
|
|
||||||
/// A codec for values of type `Set<&[u8]>`.
|
/// A codec for values of type `Set<&[u8]>`.
|
||||||
|
@ -57,7 +57,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, FacetSearchResult, Filter, FormatOptions, MatchBounds, MatcherBuilder,
|
FacetDistribution, FacetValueHit, Filter, FormatOptions, MatchBounds, MatcherBuilder,
|
||||||
MatchingWords, Search, SearchForFacetValue, SearchResult, TermsMatchingStrategy,
|
MatchingWords, Search, SearchForFacetValue, SearchResult, TermsMatchingStrategy,
|
||||||
DEFAULT_VALUES_PER_FACET,
|
DEFAULT_VALUES_PER_FACET,
|
||||||
};
|
};
|
||||||
|
@ -257,7 +257,7 @@ impl<'a> SearchForFacetValue<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(&self) -> Result<Vec<FacetSearchResult>> {
|
pub fn execute(&self) -> Result<Vec<FacetValueHit>> {
|
||||||
let index = self.search_query.index;
|
let index = self.search_query.index;
|
||||||
let rtxn = self.search_query.rtxn;
|
let rtxn = self.search_query.rtxn;
|
||||||
|
|
||||||
@ -304,7 +304,7 @@ impl<'a> SearchForFacetValue<'a> {
|
|||||||
};
|
};
|
||||||
let count = search_candidates.intersection_len(&docids);
|
let count = search_candidates.intersection_len(&docids);
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
result.push(FacetSearchResult { value: value.to_string(), count });
|
result.push(FacetValueHit { value: value.to_string(), count });
|
||||||
length += 1;
|
length += 1;
|
||||||
}
|
}
|
||||||
if length >= MAX_NUMBER_OF_FACETS {
|
if length >= MAX_NUMBER_OF_FACETS {
|
||||||
@ -327,7 +327,7 @@ impl<'a> SearchForFacetValue<'a> {
|
|||||||
};
|
};
|
||||||
let count = search_candidates.intersection_len(&docids);
|
let count = search_candidates.intersection_len(&docids);
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
result.push(FacetSearchResult { value: value.to_string(), count });
|
result.push(FacetValueHit { value: value.to_string(), count });
|
||||||
length += 1;
|
length += 1;
|
||||||
}
|
}
|
||||||
if length >= MAX_NUMBER_OF_FACETS {
|
if length >= MAX_NUMBER_OF_FACETS {
|
||||||
@ -341,8 +341,8 @@ impl<'a> SearchForFacetValue<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, Clone, serde::Serialize, PartialEq)]
|
||||||
pub struct FacetSearchResult {
|
pub struct FacetValueHit {
|
||||||
/// The original facet value
|
/// The original facet value
|
||||||
pub value: String,
|
pub value: String,
|
||||||
/// The number of documents associated to this facet
|
/// The number of documents associated to this facet
|
||||||
|
Loading…
Reference in New Issue
Block a user