2023-04-13 18:16:33 +02:00
|
|
|
use actix_web::web::Data;
|
|
|
|
use actix_web::{web, HttpRequest, HttpResponse};
|
2023-04-26 17:09:59 +02:00
|
|
|
use deserr::actix_web::AwebJson;
|
2023-04-13 18:16:33 +02:00
|
|
|
use index_scheduler::IndexScheduler;
|
2023-04-26 17:09:59 +02:00
|
|
|
use meilisearch_types::deserr::DeserrJsonError;
|
2023-04-13 18:16:33 +02:00
|
|
|
use meilisearch_types::error::deserr_codes::*;
|
|
|
|
use meilisearch_types::error::ResponseError;
|
|
|
|
use meilisearch_types::index_uid::IndexUid;
|
|
|
|
use serde_json::Value;
|
2024-02-07 15:51:38 +01:00
|
|
|
use tracing::debug;
|
2023-04-13 18:16:33 +02:00
|
|
|
|
2023-04-26 17:08:55 +02:00
|
|
|
use crate::analytics::{Analytics, FacetSearchAggregator};
|
2023-04-13 18:16:33 +02:00
|
|
|
use crate::extractors::authentication::policies::*;
|
|
|
|
use crate::extractors::authentication::GuardedData;
|
|
|
|
use crate::search::{
|
2023-12-12 21:19:48 +01:00
|
|
|
add_search_rules, perform_facet_search, HybridQuery, MatchingStrategy, SearchQuery,
|
|
|
|
DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER, DEFAULT_HIGHLIGHT_POST_TAG,
|
|
|
|
DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT, DEFAULT_SEARCH_OFFSET,
|
2023-04-13 18:16:33 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
pub fn configure(cfg: &mut web::ServiceConfig) {
|
|
|
|
cfg.service(web::resource("").route(web::post().to(search)));
|
|
|
|
}
|
|
|
|
|
2023-06-29 10:28:23 +02:00
|
|
|
/// # Important
|
|
|
|
///
|
|
|
|
/// Intentionally don't use `deny_unknown_fields` to ignore search parameters sent by user
|
2023-05-04 12:27:19 +02:00
|
|
|
#[derive(Debug, Clone, Default, PartialEq, deserr::Deserr)]
|
2023-06-22 14:59:44 +02:00
|
|
|
#[deserr(error = DeserrJsonError, rename_all = camelCase)]
|
2023-04-13 18:16:33 +02:00
|
|
|
pub struct FacetSearchQuery {
|
2023-04-26 18:09:24 +02:00
|
|
|
#[deserr(default, error = DeserrJsonError<InvalidFacetSearchQuery>)]
|
2023-04-13 18:16:33 +02:00
|
|
|
pub facet_query: Option<String>,
|
2023-06-26 10:40:54 +02:00
|
|
|
#[deserr(error = DeserrJsonError<InvalidFacetSearchFacetName>, missing_field_error = DeserrJsonError::missing_facet_search_facet_name)]
|
2023-04-13 18:16:33 +02:00
|
|
|
pub facet_name: String,
|
|
|
|
#[deserr(default, error = DeserrJsonError<InvalidSearchQ>)]
|
|
|
|
pub q: Option<String>,
|
2023-05-04 12:27:19 +02:00
|
|
|
#[deserr(default, error = DeserrJsonError<InvalidSearchVector>)]
|
|
|
|
pub vector: Option<Vec<f32>>,
|
2023-12-12 21:19:48 +01:00
|
|
|
#[deserr(default, error = DeserrJsonError<InvalidHybridQuery>)]
|
|
|
|
pub hybrid: Option<HybridQuery>,
|
2023-04-13 18:16:33 +02:00
|
|
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
|
|
|
pub filter: Option<Value>,
|
|
|
|
#[deserr(default, error = DeserrJsonError<InvalidSearchMatchingStrategy>, default)]
|
|
|
|
pub matching_strategy: MatchingStrategy,
|
2023-07-03 11:52:43 +02:00
|
|
|
#[deserr(default, error = DeserrJsonError<InvalidSearchAttributesToSearchOn>, default)]
|
2023-06-28 15:16:04 +02:00
|
|
|
pub attributes_to_search_on: Option<Vec<String>>,
|
2023-04-13 18:16:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn search(
|
|
|
|
index_scheduler: GuardedData<ActionPolicy<{ actions::SEARCH }>, Data<IndexScheduler>>,
|
|
|
|
index_uid: web::Path<String>,
|
|
|
|
params: AwebJson<FacetSearchQuery, DeserrJsonError>,
|
|
|
|
req: HttpRequest,
|
|
|
|
analytics: web::Data<dyn Analytics>,
|
|
|
|
) -> Result<HttpResponse, ResponseError> {
|
|
|
|
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
|
|
|
|
2023-04-26 11:55:43 +02:00
|
|
|
let query = params.into_inner();
|
2023-04-13 18:16:33 +02:00
|
|
|
debug!("facet search called with params: {:?}", query);
|
|
|
|
|
2023-04-26 17:08:55 +02:00
|
|
|
let mut aggregate = FacetSearchAggregator::from_query(&query, &req);
|
|
|
|
|
2023-04-13 18:16:33 +02:00
|
|
|
let facet_query = query.facet_query.clone();
|
|
|
|
let facet_name = query.facet_name.clone();
|
|
|
|
let mut search_query = SearchQuery::from(query);
|
|
|
|
|
|
|
|
// Tenant token search_rules.
|
|
|
|
if let Some(search_rules) = index_scheduler.filters().get_index_search_rules(&index_uid) {
|
|
|
|
add_search_rules(&mut search_query, search_rules);
|
|
|
|
}
|
|
|
|
|
|
|
|
let index = index_scheduler.index(&index_uid)?;
|
2023-10-23 10:38:56 +02:00
|
|
|
let features = index_scheduler.features();
|
2023-04-13 18:16:33 +02:00
|
|
|
let search_result = tokio::task::spawn_blocking(move || {
|
2023-05-04 12:27:19 +02:00
|
|
|
perform_facet_search(&index, search_query, facet_query, facet_name, features)
|
2023-04-13 18:16:33 +02:00
|
|
|
})
|
|
|
|
.await?;
|
|
|
|
|
2023-04-26 17:08:55 +02:00
|
|
|
if let Ok(ref search_result) = search_result {
|
|
|
|
aggregate.succeed(search_result);
|
|
|
|
}
|
|
|
|
analytics.post_facet_search(aggregate);
|
2023-04-13 18:16:33 +02:00
|
|
|
|
|
|
|
let search_result = search_result?;
|
|
|
|
|
|
|
|
debug!("returns: {:?}", search_result);
|
|
|
|
Ok(HttpResponse::Ok().json(search_result))
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<FacetSearchQuery> for SearchQuery {
|
|
|
|
fn from(value: FacetSearchQuery) -> Self {
|
2023-06-22 14:59:44 +02:00
|
|
|
let FacetSearchQuery {
|
|
|
|
facet_query: _,
|
|
|
|
facet_name: _,
|
|
|
|
q,
|
|
|
|
vector,
|
|
|
|
filter,
|
|
|
|
matching_strategy,
|
2023-06-28 15:16:04 +02:00
|
|
|
attributes_to_search_on,
|
2023-12-12 21:19:48 +01:00
|
|
|
hybrid,
|
2023-06-22 14:59:44 +02:00
|
|
|
} = value;
|
|
|
|
|
2023-04-13 18:16:33 +02:00
|
|
|
SearchQuery {
|
2023-06-22 14:59:44 +02:00
|
|
|
q,
|
|
|
|
offset: DEFAULT_SEARCH_OFFSET(),
|
|
|
|
limit: DEFAULT_SEARCH_LIMIT(),
|
|
|
|
page: None,
|
|
|
|
hits_per_page: None,
|
|
|
|
attributes_to_retrieve: None,
|
|
|
|
attributes_to_crop: None,
|
|
|
|
crop_length: DEFAULT_CROP_LENGTH(),
|
|
|
|
attributes_to_highlight: None,
|
|
|
|
show_matches_position: false,
|
|
|
|
show_ranking_score: false,
|
|
|
|
show_ranking_score_details: false,
|
|
|
|
filter,
|
|
|
|
sort: None,
|
|
|
|
facets: None,
|
|
|
|
highlight_pre_tag: DEFAULT_HIGHLIGHT_PRE_TAG(),
|
|
|
|
highlight_post_tag: DEFAULT_HIGHLIGHT_POST_TAG(),
|
|
|
|
crop_marker: DEFAULT_CROP_MARKER(),
|
|
|
|
matching_strategy,
|
2023-12-14 12:42:37 +01:00
|
|
|
vector,
|
2023-06-28 15:16:04 +02:00
|
|
|
attributes_to_search_on,
|
2023-12-12 21:19:48 +01:00
|
|
|
hybrid,
|
2023-04-13 18:16:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|