mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-10-23 03:56:28 +00:00
Add comprehensive metadata tests with insta snapshots
- Add 9 test cases covering single search, multi-search, and federated search - Test metadata header opt-in functionality with case insensitivity - Test header false value handling - Test UUID format validation and consistency - Use insta snapshots for reliable, maintainable test assertions - Fix header parsing to properly handle 'false' values - Add helper methods for testing with custom headers
This commit is contained in:
@@ -346,7 +346,12 @@ pub async fn search_with_url_query(
|
|||||||
search_kind(&query, index_scheduler.get_ref(), index_uid.to_string(), &index)?;
|
search_kind(&query, index_scheduler.get_ref(), index_uid.to_string(), &index)?;
|
||||||
let retrieve_vector = RetrieveVectors::new(query.retrieve_vectors);
|
let retrieve_vector = RetrieveVectors::new(query.retrieve_vectors);
|
||||||
let permit = search_queue.try_get_search_permit().await?;
|
let permit = search_queue.try_get_search_permit().await?;
|
||||||
let include_metadata = req.headers().get(INCLUDE_METADATA_HEADER).is_some();
|
let include_metadata = req
|
||||||
|
.headers()
|
||||||
|
.get(INCLUDE_METADATA_HEADER)
|
||||||
|
.and_then(|h| h.to_str().ok())
|
||||||
|
.map(|v| matches!(v.to_lowercase().as_str(), "true" | "1"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
let search_result = tokio::task::spawn_blocking(move || {
|
let search_result = tokio::task::spawn_blocking(move || {
|
||||||
perform_search(
|
perform_search(
|
||||||
@@ -459,7 +464,12 @@ pub async fn search_with_post(
|
|||||||
search_kind(&query, index_scheduler.get_ref(), index_uid.to_string(), &index)?;
|
search_kind(&query, index_scheduler.get_ref(), index_uid.to_string(), &index)?;
|
||||||
let retrieve_vectors = RetrieveVectors::new(query.retrieve_vectors);
|
let retrieve_vectors = RetrieveVectors::new(query.retrieve_vectors);
|
||||||
|
|
||||||
let include_metadata = req.headers().get(INCLUDE_METADATA_HEADER).is_some();
|
let include_metadata = req
|
||||||
|
.headers()
|
||||||
|
.get(INCLUDE_METADATA_HEADER)
|
||||||
|
.and_then(|h| h.to_str().ok())
|
||||||
|
.map(|v| matches!(v.to_lowercase().as_str(), "true" | "1"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
let permit = search_queue.try_get_search_permit().await?;
|
let permit = search_queue.try_get_search_permit().await?;
|
||||||
let search_result = tokio::task::spawn_blocking(move || {
|
let search_result = tokio::task::spawn_blocking(move || {
|
||||||
|
@@ -202,7 +202,12 @@ pub async fn multi_search_with_post(
|
|||||||
.headers()
|
.headers()
|
||||||
.get(PROXY_SEARCH_HEADER)
|
.get(PROXY_SEARCH_HEADER)
|
||||||
.is_some_and(|value| value.as_bytes() == PROXY_SEARCH_HEADER_VALUE.as_bytes());
|
.is_some_and(|value| value.as_bytes() == PROXY_SEARCH_HEADER_VALUE.as_bytes());
|
||||||
let include_metadata = req.headers().get(INCLUDE_METADATA_HEADER).is_some();
|
let include_metadata = req
|
||||||
|
.headers()
|
||||||
|
.get(INCLUDE_METADATA_HEADER)
|
||||||
|
.and_then(|h| h.to_str().ok())
|
||||||
|
.map(|v| matches!(v.to_lowercase().as_str(), "true" | "1"))
|
||||||
|
.unwrap_or(false);
|
||||||
let search_result = perform_federated_search(
|
let search_result = perform_federated_search(
|
||||||
&index_scheduler,
|
&index_scheduler,
|
||||||
queries,
|
queries,
|
||||||
@@ -230,7 +235,12 @@ pub async fn multi_search_with_post(
|
|||||||
HttpResponse::Ok().json(search_result?)
|
HttpResponse::Ok().json(search_result?)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let include_metadata = req.headers().get(INCLUDE_METADATA_HEADER).is_some();
|
let include_metadata = req
|
||||||
|
.headers()
|
||||||
|
.get(INCLUDE_METADATA_HEADER)
|
||||||
|
.and_then(|h| h.to_str().ok())
|
||||||
|
.map(|v| matches!(v.to_lowercase().as_str(), "true" | "1"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
// Explicitly expect a `(ResponseError, usize)` for the error type rather than `ResponseError` only,
|
// Explicitly expect a `(ResponseError, usize)` for the error type rather than `ResponseError` only,
|
||||||
// so that `?` doesn't work if it doesn't use `with_index`, ensuring that it is not forgotten in case of code
|
// so that `?` doesn't work if it doesn't use `with_index`, ensuring that it is not forgotten in case of code
|
||||||
|
@@ -516,6 +516,33 @@ impl<State> Index<'_, State> {
|
|||||||
self.service.post_encoded(url, query, self.encoder).await
|
self.service.post_encoded(url, query, self.encoder).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn search_with_headers(
|
||||||
|
&self,
|
||||||
|
query: Value,
|
||||||
|
headers: Vec<(&str, &str)>,
|
||||||
|
) -> (Value, StatusCode) {
|
||||||
|
let url = format!("/indexes/{}/search", urlencode(self.uid.as_ref()));
|
||||||
|
let body = serde_json::to_string(&query).unwrap();
|
||||||
|
let mut all_headers = vec![("content-type", "application/json")];
|
||||||
|
all_headers.extend(headers);
|
||||||
|
self.service.post_str(url, body, all_headers).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn multi_search_post(&self, queries: Value) -> (Value, StatusCode) {
|
||||||
|
self.service.post("/multi-search", queries).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn multi_search_post_with_headers(
|
||||||
|
&self,
|
||||||
|
queries: Value,
|
||||||
|
headers: Vec<(&str, &str)>,
|
||||||
|
) -> (Value, StatusCode) {
|
||||||
|
let body = serde_json::to_string(&queries).unwrap();
|
||||||
|
let mut all_headers = vec![("content-type", "application/json")];
|
||||||
|
all_headers.extend(headers);
|
||||||
|
self.service.post_str("/multi-search", body, all_headers).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn search_get(&self, query: &str) -> (Value, StatusCode) {
|
pub async fn search_get(&self, query: &str) -> (Value, StatusCode) {
|
||||||
let url = format!("/indexes/{}/search{}", urlencode(self.uid.as_ref()), query);
|
let url = format!("/indexes/{}/search{}", urlencode(self.uid.as_ref()), query);
|
||||||
self.service.get(url).await
|
self.service.get(url).await
|
||||||
|
@@ -390,6 +390,15 @@ impl<State> Server<State> {
|
|||||||
self.service.post("/multi-search", queries).await
|
self.service.post("/multi-search", queries).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn multi_search_post_with_headers(
|
||||||
|
&self,
|
||||||
|
queries: Value,
|
||||||
|
headers: Vec<(&str, &str)>,
|
||||||
|
) -> (Value, StatusCode) {
|
||||||
|
let body = serde_json::to_string(&queries).unwrap();
|
||||||
|
self.service.post_str("/multi-search", body, headers).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn list_indexes_raw(&self, parameters: &str) -> (Value, StatusCode) {
|
pub async fn list_indexes_raw(&self, parameters: &str) -> (Value, StatusCode) {
|
||||||
self.service.get(format!("/indexes{parameters}")).await
|
self.service.get(format!("/indexes{parameters}")).await
|
||||||
}
|
}
|
||||||
|
387
crates/meilisearch/tests/search/metadata.rs
Normal file
387
crates/meilisearch/tests/search/metadata.rs
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
use meili_snap::{json_string, snapshot};
|
||||||
|
|
||||||
|
use crate::common::{shared_index_with_documents, Server, DOCUMENTS};
|
||||||
|
use crate::json;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn search_without_metadata_header() {
|
||||||
|
let index = shared_index_with_documents().await;
|
||||||
|
|
||||||
|
// Test that metadata is not included by default
|
||||||
|
index
|
||||||
|
.search(json!({"q": "glass"}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response, { ".processingTimeMs" => "[duration]", ".requestUid" => "[uuid]" }), @r###"
|
||||||
|
{
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"title": "Gläss",
|
||||||
|
"id": "450465",
|
||||||
|
"color": [
|
||||||
|
"blue",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "glass",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn search_with_metadata_header() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (task, _code) = index.add_documents(documents, None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Test with Meili-Include-Metadata header
|
||||||
|
let (response, code) = index
|
||||||
|
.search_with_headers(json!({"q": "glass"}), vec![("Meili-Include-Metadata", "true")])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response, { ".processingTimeMs" => "[duration]", ".requestUid" => "[uuid]", ".metadata.queryUid" => "[uuid]" }), @r###"
|
||||||
|
{
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"title": "Gläss",
|
||||||
|
"id": "450465",
|
||||||
|
"color": [
|
||||||
|
"blue",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "glass",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]",
|
||||||
|
"metadata": {
|
||||||
|
"queryUid": "[uuid]",
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"primaryKey": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn search_with_metadata_header_and_primary_key() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (task, _code) = index.add_documents(documents, Some("id")).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Test with Meili-Include-Metadata header
|
||||||
|
let (response, code) = index
|
||||||
|
.search_with_headers(json!({"q": "glass"}), vec![("Meili-Include-Metadata", "true")])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response, { ".processingTimeMs" => "[duration]", ".requestUid" => "[uuid]", ".metadata.queryUid" => "[uuid]" }), @r###"
|
||||||
|
{
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"id": "450465",
|
||||||
|
"title": "Gläss",
|
||||||
|
"color": [
|
||||||
|
"blue",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "glass",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]",
|
||||||
|
"metadata": {
|
||||||
|
"queryUid": "[uuid]",
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"primaryKey": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn multi_search_without_metadata_header() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (task, _code) = index.add_documents(documents, None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Test multi-search without metadata header
|
||||||
|
let (response, code) = index
|
||||||
|
.multi_search_post(json!({
|
||||||
|
"queries": [
|
||||||
|
{"indexUid": index.uid, "q": "glass"},
|
||||||
|
{"indexUid": index.uid, "q": "dragon"}
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response, { ".results[0].processingTimeMs" => "[duration]", ".results[0].requestUid" => "[uuid]", ".results[1].processingTimeMs" => "[duration]", ".results[1].requestUid" => "[uuid]" }), @r###"
|
||||||
|
{
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"title": "Gläss",
|
||||||
|
"id": "450465",
|
||||||
|
"color": [
|
||||||
|
"blue",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "glass",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"title": "How to Train Your Dragon: The Hidden World",
|
||||||
|
"id": "166428",
|
||||||
|
"color": [
|
||||||
|
"green",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "dragon",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn multi_search_with_metadata_header() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (task, _code) = index.add_documents(documents, Some("id")).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Test multi-search with metadata header
|
||||||
|
let (response, code) = index
|
||||||
|
.multi_search_post_with_headers(
|
||||||
|
json!({
|
||||||
|
"queries": [
|
||||||
|
{"indexUid": index.uid, "q": "glass"},
|
||||||
|
{"indexUid": index.uid, "q": "dragon"}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
vec![("Meili-Include-Metadata", "true")],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response, { ".results[0].processingTimeMs" => "[duration]", ".results[0].requestUid" => "[uuid]", ".results[0].metadata.queryUid" => "[uuid]", ".results[1].processingTimeMs" => "[duration]", ".results[1].requestUid" => "[uuid]", ".results[1].metadata.queryUid" => "[uuid]" }), @r###"
|
||||||
|
{
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"id": "450465",
|
||||||
|
"title": "Gläss",
|
||||||
|
"color": [
|
||||||
|
"blue",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "glass",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]",
|
||||||
|
"metadata": {
|
||||||
|
"queryUid": "[uuid]",
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"primaryKey": "id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"id": "166428",
|
||||||
|
"title": "How to Train Your Dragon: The Hidden World",
|
||||||
|
"color": [
|
||||||
|
"green",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "dragon",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]",
|
||||||
|
"metadata": {
|
||||||
|
"queryUid": "[uuid]",
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"primaryKey": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn search_metadata_header_false_value() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (task, _code) = index.add_documents(documents, None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Test with header set to false
|
||||||
|
let (response, code) = index
|
||||||
|
.search_with_headers(json!({"q": "glass"}), vec![("Meili-Include-Metadata", "false")])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response, { ".processingTimeMs" => "[duration]", ".requestUid" => "[uuid]" }), @r###"
|
||||||
|
{
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"title": "Gläss",
|
||||||
|
"id": "450465",
|
||||||
|
"color": [
|
||||||
|
"blue",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "glass",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn search_metadata_uuid_format() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (task, _code) = index.add_documents(documents, None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.search_with_headers(json!({"q": "glass"}), vec![("Meili-Include-Metadata", "true")])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response, { ".processingTimeMs" => "[duration]", ".requestUid" => "[uuid]", ".metadata.queryUid" => "[uuid]" }), @r###"
|
||||||
|
{
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"title": "Gläss",
|
||||||
|
"id": "450465",
|
||||||
|
"color": [
|
||||||
|
"blue",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "glass",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]",
|
||||||
|
"metadata": {
|
||||||
|
"queryUid": "[uuid]",
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"primaryKey": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn search_metadata_consistency_across_requests() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (task, _code) = index.add_documents(documents, Some("id")).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Make multiple requests and check that metadata is consistent
|
||||||
|
for _i in 0..3 {
|
||||||
|
let (response, code) = index
|
||||||
|
.search_with_headers(json!({"q": "glass"}), vec![("Meili-Include-Metadata", "true")])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response, { ".processingTimeMs" => "[duration]", ".requestUid" => "[uuid]", ".metadata.queryUid" => "[uuid]" }), @r###"
|
||||||
|
{
|
||||||
|
"hits": [
|
||||||
|
{
|
||||||
|
"id": "450465",
|
||||||
|
"title": "Gläss",
|
||||||
|
"color": [
|
||||||
|
"blue",
|
||||||
|
"red"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "glass",
|
||||||
|
"processingTimeMs": "[duration]",
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0,
|
||||||
|
"estimatedTotalHits": 1,
|
||||||
|
"requestUid": "[uuid]",
|
||||||
|
"metadata": {
|
||||||
|
"queryUid": "[uuid]",
|
||||||
|
"indexUid": "[uuid]",
|
||||||
|
"primaryKey": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
}
|
@@ -11,6 +11,7 @@ mod hybrid;
|
|||||||
#[cfg(not(feature = "chinese-pinyin"))]
|
#[cfg(not(feature = "chinese-pinyin"))]
|
||||||
mod locales;
|
mod locales;
|
||||||
mod matching_strategy;
|
mod matching_strategy;
|
||||||
|
mod metadata;
|
||||||
mod multi;
|
mod multi;
|
||||||
mod pagination;
|
mod pagination;
|
||||||
mod restrict_searchable;
|
mod restrict_searchable;
|
||||||
|
Reference in New Issue
Block a user