mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-01-18 17:11:15 +08:00
Merge #2678
2678: Accept either an array of documents or a single document r=irevoire a=Kerollmops # Pull Request ## What does this PR do? Fixes #2671 ## PR checklist Please check if your PR fulfills the following requirements: - [x] Does this PR fix an existing issue? - [x] Have you read the contributing guidelines? - [x] Have you made sure that the title is accurate and descriptive of the changes? Co-authored-by: Clément Renault <clement@meilisearch.com>
This commit is contained in:
commit
cb29d7d124
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"
|
||||||
|
@ -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