mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-01-18 08:48:32 +08:00
Merge branch 'main' into metrics/prometheus-setup
This commit is contained in:
commit
099abefc6d
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -983,6 +983,9 @@ name = "either"
|
|||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-hal"
|
name = "embedded-hal"
|
||||||
|
@ -158,7 +158,14 @@ pub async fn delete_index(
|
|||||||
pub async fn get_index_stats(
|
pub async fn get_index_stats(
|
||||||
meilisearch: GuardedData<ActionPolicy<{ actions::STATS_GET }>, MeiliSearch>,
|
meilisearch: GuardedData<ActionPolicy<{ actions::STATS_GET }>, MeiliSearch>,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
|
req: HttpRequest,
|
||||||
|
analytics: web::Data<dyn Analytics>,
|
||||||
) -> Result<HttpResponse, ResponseError> {
|
) -> Result<HttpResponse, ResponseError> {
|
||||||
|
analytics.publish(
|
||||||
|
"Stats Seen".to_string(),
|
||||||
|
json!({ "per_index_uid": true }),
|
||||||
|
Some(&req),
|
||||||
|
);
|
||||||
let response = meilisearch.get_index_stats(path.into_inner()).await?;
|
let response = meilisearch.get_index_stats(path.into_inner()).await?;
|
||||||
|
|
||||||
debug!("returns: {:?}", response);
|
debug!("returns: {:?}", response);
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use actix_web::http::header::{self};
|
use actix_web::http::header::{self};
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use serde_json::json;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use meilisearch_lib::index::{Settings, Unchecked};
|
use meilisearch_lib::index::{Settings, Unchecked};
|
||||||
@ -10,6 +11,7 @@ use meilisearch_lib::MeiliSearch;
|
|||||||
use meilisearch_types::error::ResponseError;
|
use meilisearch_types::error::ResponseError;
|
||||||
use meilisearch_types::star_or::StarOr;
|
use meilisearch_types::star_or::StarOr;
|
||||||
|
|
||||||
|
use crate::analytics::Analytics;
|
||||||
use crate::extractors::authentication::{policies::*, GuardedData};
|
use crate::extractors::authentication::{policies::*, GuardedData};
|
||||||
use prometheus::{Encoder, TextEncoder};
|
use prometheus::{Encoder, TextEncoder};
|
||||||
|
|
||||||
@ -233,7 +235,14 @@ pub async fn running() -> HttpResponse {
|
|||||||
|
|
||||||
async fn get_stats(
|
async fn get_stats(
|
||||||
meilisearch: GuardedData<ActionPolicy<{ actions::STATS_GET }>, MeiliSearch>,
|
meilisearch: GuardedData<ActionPolicy<{ actions::STATS_GET }>, MeiliSearch>,
|
||||||
|
req: HttpRequest,
|
||||||
|
analytics: web::Data<dyn Analytics>,
|
||||||
) -> Result<HttpResponse, ResponseError> {
|
) -> Result<HttpResponse, ResponseError> {
|
||||||
|
analytics.publish(
|
||||||
|
"Stats Seen".to_string(),
|
||||||
|
json!({ "per_index_uid": false }),
|
||||||
|
Some(&req),
|
||||||
|
);
|
||||||
let search_rules = &meilisearch.filters().search_rules;
|
let search_rules = &meilisearch.filters().search_rules;
|
||||||
let response = meilisearch.get_all_stats(search_rules).await?;
|
let response = meilisearch.get_all_stats(search_rules).await?;
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ use std::str::FromStr;
|
|||||||
use std::write;
|
use std::write;
|
||||||
|
|
||||||
use meilisearch_lib::index::{Settings, Unchecked};
|
use meilisearch_lib::index::{Settings, Unchecked};
|
||||||
use meilisearch_lib::tasks::batch::BatchId;
|
|
||||||
use meilisearch_lib::tasks::task::{
|
use meilisearch_lib::tasks::task::{
|
||||||
DocumentDeletion, Task, TaskContent, TaskEvent, TaskId, TaskResult,
|
DocumentDeletion, Task, TaskContent, TaskEvent, TaskId, TaskResult,
|
||||||
};
|
};
|
||||||
@ -12,8 +11,6 @@ use meilisearch_types::error::ResponseError;
|
|||||||
use serde::{Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
|
|
||||||
use crate::AUTOBATCHING_ENABLED;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum TaskType {
|
pub enum TaskType {
|
||||||
@ -230,8 +227,6 @@ pub struct TaskView {
|
|||||||
started_at: Option<OffsetDateTime>,
|
started_at: Option<OffsetDateTime>,
|
||||||
#[serde(serialize_with = "time::serde::rfc3339::option::serialize")]
|
#[serde(serialize_with = "time::serde::rfc3339::option::serialize")]
|
||||||
finished_at: Option<OffsetDateTime>,
|
finished_at: Option<OffsetDateTime>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
batch_uid: Option<BatchId>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Task> for TaskView {
|
impl From<Task> for TaskView {
|
||||||
@ -380,16 +375,6 @@ impl From<Task> for TaskView {
|
|||||||
|
|
||||||
let duration = finished_at.zip(started_at).map(|(tf, ts)| (tf - ts));
|
let duration = finished_at.zip(started_at).map(|(tf, ts)| (tf - ts));
|
||||||
|
|
||||||
let batch_uid = AUTOBATCHING_ENABLED
|
|
||||||
.load(std::sync::atomic::Ordering::Relaxed)
|
|
||||||
.then(|| {
|
|
||||||
events.iter().find_map(|e| match e {
|
|
||||||
TaskEvent::Batched { batch_id, .. } => Some(*batch_id),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
uid: id,
|
uid: id,
|
||||||
index_uid,
|
index_uid,
|
||||||
@ -401,7 +386,6 @@ impl From<Task> for TaskView {
|
|||||||
enqueued_at,
|
enqueued_at,
|
||||||
started_at,
|
started_at,
|
||||||
finished_at,
|
finished_at,
|
||||||
batch_uid,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,51 @@ async fn add_documents_test_json_content_types() {
|
|||||||
assert_eq!(response["taskUid"], 1);
|
assert_eq!(response["taskUid"], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Here we try to send a single document instead of an array with a single document inside.
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn add_single_document_test_json_content_types() {
|
||||||
|
let document = json!({
|
||||||
|
"id": 1,
|
||||||
|
"content": "Bouvier Bernois",
|
||||||
|
});
|
||||||
|
|
||||||
|
// this is a what is expected and should work
|
||||||
|
let server = Server::new().await;
|
||||||
|
let app = test::init_service(create_app!(
|
||||||
|
&server.service.meilisearch,
|
||||||
|
&server.service.auth,
|
||||||
|
true,
|
||||||
|
&server.service.options,
|
||||||
|
analytics::MockAnalytics::new(&server.service.options).0
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
// post
|
||||||
|
let req = test::TestRequest::post()
|
||||||
|
.uri("/indexes/dog/documents")
|
||||||
|
.set_payload(document.to_string())
|
||||||
|
.insert_header(("content-type", "application/json"))
|
||||||
|
.to_request();
|
||||||
|
let res = test::call_service(&app, req).await;
|
||||||
|
let status_code = res.status();
|
||||||
|
let body = test::read_body(res).await;
|
||||||
|
let response: Value = serde_json::from_slice(&body).unwrap_or_default();
|
||||||
|
assert_eq!(status_code, 202);
|
||||||
|
assert_eq!(response["taskUid"], 0);
|
||||||
|
|
||||||
|
// put
|
||||||
|
let req = test::TestRequest::put()
|
||||||
|
.uri("/indexes/dog/documents")
|
||||||
|
.set_payload(document.to_string())
|
||||||
|
.insert_header(("content-type", "application/json"))
|
||||||
|
.to_request();
|
||||||
|
let res = test::call_service(&app, req).await;
|
||||||
|
let status_code = res.status();
|
||||||
|
let body = test::read_body(res).await;
|
||||||
|
let response: Value = serde_json::from_slice(&body).unwrap_or_default();
|
||||||
|
assert_eq!(status_code, 202);
|
||||||
|
assert_eq!(response["taskUid"], 1);
|
||||||
|
}
|
||||||
|
|
||||||
/// any other content-type is must be refused
|
/// any other content-type is must be refused
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn error_add_documents_test_bad_content_types() {
|
async fn error_add_documents_test_bad_content_types() {
|
||||||
@ -327,7 +372,7 @@ async fn error_add_malformed_json_documents() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
response["message"],
|
response["message"],
|
||||||
json!(
|
json!(
|
||||||
r#"The `json` payload provided is malformed. `Couldn't serialize document value: invalid type: string "0123456789012345678901234567...890123456789012345678901234567890123456789", expected a sequence at line 1 column 102`."#
|
r#"The `json` payload provided is malformed. `Couldn't serialize document value: data did not match any variant of untagged enum Either`."#
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(response["code"], json!("malformed_payload"));
|
assert_eq!(response["code"], json!("malformed_payload"));
|
||||||
@ -350,7 +395,7 @@ async fn error_add_malformed_json_documents() {
|
|||||||
assert_eq!(status_code, 400);
|
assert_eq!(status_code, 400);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
response["message"],
|
response["message"],
|
||||||
json!("The `json` payload provided is malformed. `Couldn't serialize document value: invalid type: string \"0123456789012345678901234567...90123456789012345678901234567890123456789m\", expected a sequence at line 1 column 103`.")
|
json!("The `json` payload provided is malformed. `Couldn't serialize document value: data did not match any variant of untagged enum Either`.")
|
||||||
);
|
);
|
||||||
assert_eq!(response["code"], json!("malformed_payload"));
|
assert_eq!(response["code"], json!("malformed_payload"));
|
||||||
assert_eq!(response["type"], json!("invalid_request"));
|
assert_eq!(response["type"], json!("invalid_request"));
|
||||||
|
@ -15,7 +15,7 @@ clap = { version = "3.1.6", features = ["derive", "env"] }
|
|||||||
crossbeam-channel = "0.5.2"
|
crossbeam-channel = "0.5.2"
|
||||||
csv = "1.1.6"
|
csv = "1.1.6"
|
||||||
derivative = "2.2.0"
|
derivative = "2.2.0"
|
||||||
either = "1.6.1"
|
either = { version = "1.6.1", features = ["serde"] }
|
||||||
flate2 = "1.0.22"
|
flate2 = "1.0.22"
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
fst = "0.4.7"
|
fst = "0.4.7"
|
||||||
|
@ -2,9 +2,12 @@ use std::borrow::Borrow;
|
|||||||
use std::fmt::{self, Debug, Display};
|
use std::fmt::{self, Debug, Display};
|
||||||
use std::io::{self, BufReader, Read, Seek, Write};
|
use std::io::{self, BufReader, Read, Seek, Write};
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
use meilisearch_types::error::{Code, ErrorCode};
|
use meilisearch_types::error::{Code, ErrorCode};
|
||||||
use meilisearch_types::internal_error;
|
use meilisearch_types::internal_error;
|
||||||
use milli::documents::{DocumentsBatchBuilder, Error};
|
use milli::documents::{DocumentsBatchBuilder, Error};
|
||||||
|
use milli::Object;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, DocumentFormatError>;
|
type Result<T> = std::result::Result<T, DocumentFormatError>;
|
||||||
|
|
||||||
@ -124,11 +127,18 @@ pub fn read_json(input: impl Read, writer: impl Write + Seek) -> Result<usize> {
|
|||||||
let mut builder = DocumentsBatchBuilder::new(writer);
|
let mut builder = DocumentsBatchBuilder::new(writer);
|
||||||
let reader = BufReader::new(input);
|
let reader = BufReader::new(input);
|
||||||
|
|
||||||
let objects: Vec<_> = serde_json::from_reader(reader)
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct ArrayOrSingleObject {
|
||||||
|
#[serde(with = "either::serde_untagged")]
|
||||||
|
inner: Either<Vec<Object>, Object>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let content: ArrayOrSingleObject = serde_json::from_reader(reader)
|
||||||
.map_err(Error::Json)
|
.map_err(Error::Json)
|
||||||
.map_err(|e| (PayloadType::Json, e))?;
|
.map_err(|e| (PayloadType::Json, e))?;
|
||||||
|
|
||||||
for object in objects {
|
for object in content.inner.map_right(|o| vec![o]).into_inner() {
|
||||||
builder
|
builder
|
||||||
.append_json_object(&object)
|
.append_json_object(&object)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
|
Loading…
Reference in New Issue
Block a user