From 4c2af8e5151eaa0f3fc09187b01a98afd762eca6 Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 19 May 2020 18:13:02 +0200 Subject: [PATCH 01/18] add error code abstractions --- meilisearch-error/Cargo.toml | 10 +++ meilisearch-error/src/lib.rs | 122 +++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 meilisearch-error/Cargo.toml create mode 100644 meilisearch-error/src/lib.rs diff --git a/meilisearch-error/Cargo.toml b/meilisearch-error/Cargo.toml new file mode 100644 index 000000000..1be97bdb8 --- /dev/null +++ b/meilisearch-error/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "meilisearch-error" +version = "0.10.1" +authors = ["mpostma "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +actix-http = "1.0.1" diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs new file mode 100644 index 000000000..d82b81b1b --- /dev/null +++ b/meilisearch-error/src/lib.rs @@ -0,0 +1,122 @@ +#![allow(dead_code)] + +use std::fmt; +use actix_http::http::StatusCode; + +pub trait ErrorCode: std::error::Error { + fn error_code(&self) -> Code; +} + +enum ErrorCategory { + None = 0, +} + +pub enum Code { + Other, +} + +impl fmt::Display for Code { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.internal().fmt(f) + } +} + +/// Internal structure providing a convenient way to create error codes +struct ErrCode { + public: bool, + fatal: bool, + http_code: StatusCode, + category: ErrorCategory, + code: u16, +} + +impl ErrCode { + fn new( + public: bool, + fatal: bool, + http_code: StatusCode, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode { + public, + fatal, + http_code, + category, + code, + } + } + + pub fn internal( + public: bool, + fatal: bool, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode::new(public, fatal, StatusCode::INTERNAL_SERVER_ERROR, category, code) + } + + pub fn forbidden( + public: bool, + fatal: bool, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode::new(public, fatal, StatusCode::FORBIDDEN, category, code) + } + + pub fn unauthorized( + public: bool, + fatal: bool, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode::new(public, fatal, StatusCode::UNAUTHORIZED, category, code) + } + + pub fn not_found( + public: bool, + fatal: bool, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode::new(public, fatal, StatusCode::NOT_FOUND, category, code) + } +} + +impl Code { + + /// ascociate a `Code` variant to the actual ErrCode + fn err_code(&self) -> ErrCode { + use Code::*; + + match self { + Other => ErrCode::not_found(false, false, ErrorCategory::None, 0), + } + } + + /// return the HTTP status code ascociated with the `Code` + pub fn http(&self) -> StatusCode { + self.err_code().http_code + } + + /// returns internal error code, in the form: + /// `EPFCNN` + /// - E: plain letter "E", to mark an error code, future main introduce W for warning + /// - P: scope of the error, 0 for private, 1 for public. Private are error that make no sense + /// reporting to the user, they are internal errors, and there is nothing the user can do about + /// them. they are nonetheless returned, without a message, for assistance purpose. + /// - F: 0 or 1, report if the error is fatal. + /// - C: error category, number in 0-9, the category of the error. Categories are still to be determined, input is required. + /// - NN: The error number, two digits, within C. + + + pub fn internal(&self) -> String { + let ErrCode { public, fatal, category, code, .. } = self.err_code(); + format!("E{}{}{}{}", + public as u16, + fatal as u16, + category as u16, + code) + } +} From e2db197b3f80f025f7339e722cd2a96d16994222 Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 19 May 2020 18:20:29 +0200 Subject: [PATCH 02/18] change ResponseError to Error --- meilisearch-http/src/error.rs | 128 +++++++++--------- .../src/helpers/authentication.rs | 18 +-- meilisearch-http/src/helpers/meilisearch.rs | 14 +- meilisearch-http/src/routes/document.rs | 42 +++--- meilisearch-http/src/routes/health.rs | 12 +- meilisearch-http/src/routes/index.rs | 58 ++++---- meilisearch-http/src/routes/search.rs | 10 +- meilisearch-http/src/routes/setting.rs | 82 +++++------ meilisearch-http/src/routes/stats.rs | 12 +- meilisearch-http/src/routes/stop_words.rs | 14 +- meilisearch-http/src/routes/synonym.rs | 14 +- 11 files changed, 203 insertions(+), 201 deletions(-) diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index 27626c80b..e59848d3d 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -7,7 +7,7 @@ use serde_json::json; use actix_web::error::JsonPayloadError; #[derive(Debug)] -pub enum ResponseError { +pub enum Error { BadParameter(String, String), BadRequest(String), CreateIndex(String), @@ -63,69 +63,69 @@ impl fmt::Display for FacetCountError { } } -impl ResponseError { - pub fn internal(err: impl fmt::Display) -> ResponseError { - ResponseError::Internal(err.to_string()) +impl Error { + pub fn internal(err: impl fmt::Display) -> Error { + Error::Internal(err.to_string()) } - pub fn bad_request(err: impl fmt::Display) -> ResponseError { - ResponseError::BadRequest(err.to_string()) + pub fn bad_request(err: impl fmt::Display) -> Error { + Error::BadRequest(err.to_string()) } - pub fn missing_authorization_header() -> ResponseError { - ResponseError::MissingAuthorizationHeader + pub fn missing_authorization_header() -> Error { + Error::MissingAuthorizationHeader } - pub fn invalid_token(err: impl fmt::Display) -> ResponseError { - ResponseError::InvalidToken(err.to_string()) + pub fn invalid_token(err: impl fmt::Display) -> Error { + Error::InvalidToken(err.to_string()) } - pub fn not_found(err: impl fmt::Display) -> ResponseError { - ResponseError::NotFound(err.to_string()) + pub fn not_found(err: impl fmt::Display) -> Error { + Error::NotFound(err.to_string()) } - pub fn index_not_found(err: impl fmt::Display) -> ResponseError { - ResponseError::IndexNotFound(err.to_string()) + pub fn index_not_found(err: impl fmt::Display) -> Error { + Error::IndexNotFound(err.to_string()) } - pub fn document_not_found(err: impl fmt::Display) -> ResponseError { - ResponseError::DocumentNotFound(err.to_string()) + pub fn document_not_found(err: impl fmt::Display) -> Error { + Error::DocumentNotFound(err.to_string()) } - pub fn missing_header(err: impl fmt::Display) -> ResponseError { - ResponseError::MissingHeader(err.to_string()) + pub fn missing_header(err: impl fmt::Display) -> Error { + Error::MissingHeader(err.to_string()) } - pub fn bad_parameter(param: impl fmt::Display, err: impl fmt::Display) -> ResponseError { - ResponseError::BadParameter(param.to_string(), err.to_string()) + pub fn bad_parameter(param: impl fmt::Display, err: impl fmt::Display) -> Error { + Error::BadParameter(param.to_string(), err.to_string()) } - pub fn open_index(err: impl fmt::Display) -> ResponseError { - ResponseError::OpenIndex(err.to_string()) + pub fn open_index(err: impl fmt::Display) -> Error { + Error::OpenIndex(err.to_string()) } - pub fn create_index(err: impl fmt::Display) -> ResponseError { - ResponseError::CreateIndex(err.to_string()) + pub fn create_index(err: impl fmt::Display) -> Error { + Error::CreateIndex(err.to_string()) } - pub fn invalid_index_uid() -> ResponseError { - ResponseError::InvalidIndexUid + pub fn invalid_index_uid() -> Error { + Error::InvalidIndexUid } - pub fn maintenance() -> ResponseError { - ResponseError::Maintenance + pub fn maintenance() -> Error { + Error::Maintenance } - pub fn retrieve_document(doc_id: u32, err: impl fmt::Display) -> ResponseError { - ResponseError::RetrieveDocument(doc_id, err.to_string()) + pub fn retrieve_document(doc_id: u64, err: impl fmt::Display) -> Error { + Error::RetrieveDocument(doc_id, err.to_string()) } - pub fn search_documents(err: impl fmt::Display) -> ResponseError { - ResponseError::SearchDocuments(err.to_string()) + pub fn search_documents(err: impl fmt::Display) -> Error { + Error::SearchDocuments(err.to_string()) } } -impl fmt::Display for ResponseError { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err), @@ -152,7 +152,7 @@ impl fmt::Display for ResponseError { } } -impl aweb::error::ResponseError for ResponseError { +impl aweb::error::ResponseError for Error { fn error_response(&self) -> aweb::HttpResponse { ResponseBuilder::new(self.status_code()).json(json!({ "message": self.to_string(), @@ -185,26 +185,26 @@ impl aweb::error::ResponseError for ResponseError { } } -impl From for ResponseError { - fn from(err: meilisearch_core::HeedError) -> ResponseError { - ResponseError::Internal(err.to_string()) +impl From for Error { + fn from(err: meilisearch_core::HeedError) -> Error { + Error::Internal(err.to_string()) } } -impl From for ResponseError { - fn from(err: meilisearch_core::FstError) -> ResponseError { - ResponseError::Internal(err.to_string()) +impl From for Error { + fn from(err: meilisearch_core::FstError) -> Error { + Error::Internal(err.to_string()) } } -impl From for ResponseError { - fn from(error: meilisearch_core::FacetError) -> ResponseError { - ResponseError::FacetExpression(error.to_string()) +impl From for Error { + fn from(error: meilisearch_core::FacetError) -> Error { + Error::FacetExpression(error.to_string()) } } -impl From for ResponseError { - fn from(err: meilisearch_core::Error) -> ResponseError { +impl From for Error { + fn from(err: meilisearch_core::Error) -> Error { use meilisearch_core::pest_error::LineColLocation::*; match err { meilisearch_core::Error::FilterParseError(e) => { @@ -214,43 +214,43 @@ impl From for ResponseError { }; let message = format!("parsing error on line {} at column {}: {}", line, column, e.variant.message()); - ResponseError::FilterParsing(message) + Error::FilterParsing(message) }, - meilisearch_core::Error::FacetError(e) => ResponseError::FacetExpression(e.to_string()), - _ => ResponseError::Internal(err.to_string()), + meilisearch_core::Error::FacetError(e) => Error::FacetExpression(e.to_string()), + _ => Error::Internal(err.to_string()), } } } -impl From for ResponseError { - fn from(err: meilisearch_schema::Error) -> ResponseError { - ResponseError::Internal(err.to_string()) +impl From for Error { + fn from(err: meilisearch_schema::Error) -> Error { + Error::Internal(err.to_string()) } } -impl From for ResponseError { - fn from(err: actix_http::Error) -> ResponseError { - ResponseError::Internal(err.to_string()) +impl From for Error { + fn from(err: actix_http::Error) -> Error { + Error::Internal(err.to_string()) } } -impl From for ResponseError { - fn from(other: FacetCountError) -> ResponseError { - ResponseError::FacetCount(other.to_string()) +impl From for Error { + fn from(other: FacetCountError) -> Error { + Error::FacetCount(other.to_string()) } } -impl From for ResponseError { - fn from(err: JsonPayloadError) -> ResponseError { +impl From for Error { + fn from(err: JsonPayloadError) -> Error { match err { - JsonPayloadError::Deserialize(err) => ResponseError::BadRequest(format!("Invalid JSON: {}", err)), - JsonPayloadError::Overflow => ResponseError::PayloadTooLarge, - JsonPayloadError::ContentType => ResponseError::UnsupportedMediaType, - JsonPayloadError::Payload(err) => ResponseError::BadRequest(format!("Problem while decoding the request: {}", err)), + JsonPayloadError::Deserialize(err) => Error::BadRequest(format!("Invalid JSON: {}", err)), + JsonPayloadError::Overflow => Error::PayloadTooLarge, + JsonPayloadError::ContentType => Error::UnsupportedMediaType, + JsonPayloadError::Payload(err) => Error::BadRequest(format!("Problem while decoding the request: {}", err)), } } } -pub fn json_error_handler(err: JsonPayloadError) -> ResponseError { +pub fn json_error_handler(err: JsonPayloadError) -> Error { err.into() } diff --git a/meilisearch-http/src/helpers/authentication.rs b/meilisearch-http/src/helpers/authentication.rs index 894718d53..4b921a783 100644 --- a/meilisearch-http/src/helpers/authentication.rs +++ b/meilisearch-http/src/helpers/authentication.rs @@ -4,10 +4,10 @@ use std::rc::Rc; use std::task::{Context, Poll}; use actix_service::{Service, Transform}; -use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; +use actix_web::{dev::ServiceRequest, dev::ServiceResponse}; use futures::future::{err, ok, Future, Ready}; -use crate::error::ResponseError; +use crate::error::Error; use crate::Data; #[derive(Clone)] @@ -19,13 +19,13 @@ pub enum Authentication { impl Transform for Authentication where - S: Service, Error = Error>, + S: Service, Error = actix_web::Error>, S::Future: 'static, B: 'static, { type Request = ServiceRequest; type Response = ServiceResponse; - type Error = Error; + type Error = actix_web::Error; type InitError = (); type Transform = LoggingMiddleware; type Future = Ready>; @@ -45,13 +45,13 @@ pub struct LoggingMiddleware { impl Service for LoggingMiddleware where - S: Service, Error = Error> + 'static, + S: Service, Error = actix_web::Error> + 'static, S::Future: 'static, B: 'static, { type Request = ServiceRequest; type Response = ServiceResponse; - type Error = Error; + type Error = actix_web::Error; type Future = Pin>>>; fn poll_ready(&mut self, cx: &mut Context) -> Poll> { @@ -71,10 +71,10 @@ where let auth_header = match req.headers().get("X-Meili-API-Key") { Some(auth) => match auth.to_str() { Ok(auth) => auth, - Err(_) => return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())), + Err(_) => return Box::pin(err(Error::MissingAuthorizationHeader.into())), }, None => { - return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())); + return Box::pin(err(Error::MissingAuthorizationHeader.into())); } }; @@ -95,7 +95,7 @@ where Box::pin(svc.call(req)) } else { Box::pin(err( - ResponseError::InvalidToken(auth_header.to_string()).into() + Error::InvalidToken(auth_header.to_string()).into() )) } } diff --git a/meilisearch-http/src/helpers/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index b6ae2c6b8..297bb1f54 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -17,7 +17,7 @@ use serde_json::Value; use siphasher::sip::SipHasher; use slice_group_by::GroupBy; -use crate::error::ResponseError; +use crate::error::Error; pub trait IndexSearchExt { fn new_search(&self, query: String) -> SearchBuilder; @@ -107,12 +107,12 @@ impl<'a> SearchBuilder<'a> { self } - pub fn search(self, reader: &heed::RoTxn) -> Result { + pub fn search(self, reader: &heed::RoTxn) -> Result { let schema = self .index .main .schema(reader)? - .ok_or(ResponseError::internal("missing schema"))?; + .ok_or(Error::internal("missing schema"))?; let ranked_map = self.index.main.ranked_map(reader)?.unwrap_or_default(); @@ -159,7 +159,7 @@ impl<'a> SearchBuilder<'a> { let start = Instant::now(); let result = query_builder.query(reader, &self.query, self.offset..(self.offset + self.limit)); - let search_result = result.map_err(ResponseError::search_documents)?; + let search_result = result.map_err(Error::search_documents)?; let time_ms = start.elapsed().as_millis() as usize; let mut all_attributes: HashSet<&str> = HashSet::new(); @@ -194,8 +194,8 @@ impl<'a> SearchBuilder<'a> { let mut document: IndexMap = self .index .document(reader, Some(&all_attributes), doc.id) - .map_err(|e| ResponseError::retrieve_document(doc.id.0, e))? - .ok_or(ResponseError::internal( + .map_err(|e| Error::retrieve_document(doc.id.0, e))? + .ok_or(Error::internal( "Impossible to retrieve the document; Corrupted data", ))?; @@ -260,7 +260,7 @@ impl<'a> SearchBuilder<'a> { reader: &heed::RoTxn, ranked_map: &'a RankedMap, schema: &Schema, - ) -> Result>, ResponseError> { + ) -> Result>, Error> { let ranking_rules = self.index.main.ranking_rules(reader)?; if let Some(ranking_rules) = ranking_rules { diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index eca88a590..d2985f286 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -7,7 +7,7 @@ use meilisearch_core::update; use serde::Deserialize; use serde_json::Value; -use crate::error::ResponseError; +use crate::error::Error; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; @@ -37,11 +37,11 @@ pub fn services(cfg: &mut web::ServiceConfig) { async fn get_document( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let internal_id = index.main @@ -49,8 +49,8 @@ async fn get_document( .ok_or(ResponseError::document_not_found(&path.document_id))?; let response: Document = index - .document(&reader, None, internal_id)? - .ok_or(ResponseError::document_not_found(&path.document_id))?; + .document(&reader, None, document_id)? + .ok_or(Error::document_not_found(&path.document_id))?; Ok(HttpResponse::Ok().json(response)) } @@ -62,11 +62,13 @@ async fn get_document( async fn delete_document( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; + + let document_id = meilisearch_core::serde::compute_document_id(&path.document_id); let mut update_writer = data.db.update_write_txn()?; @@ -93,11 +95,11 @@ async fn get_all_documents( data: web::Data, path: web::Path, params: web::Query, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let offset = params.offset.unwrap_or(0); let limit = params.limit.unwrap_or(20); @@ -151,18 +153,18 @@ async fn update_multiple_documents( params: web::Query, body: web::Json>, is_partial: bool, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let mut schema = index .main .schema(&reader)? - .ok_or(ResponseError::internal("Impossible to retrieve the schema"))?; + .ok_or(Error::internal("Impossible to retrieve the schema"))?; if schema.primary_key().is_none() { let id = match ¶ms.primary_key { @@ -170,14 +172,14 @@ async fn update_multiple_documents( None => body .first() .and_then(find_primary_key) - .ok_or(ResponseError::bad_request("Could not infer a primary key"))?, + .ok_or(Error::bad_request("Could not infer a primary key"))?, }; let mut writer = data.db.main_write_txn()?; schema .set_primary_key(&id) - .map_err(ResponseError::bad_request)?; + .map_err(Error::bad_request)?; index.main.put_schema(&mut writer, &schema)?; writer.commit()?; } @@ -205,7 +207,7 @@ async fn add_documents( path: web::Path, params: web::Query, body: web::Json>, -) -> Result { +) -> Result { update_multiple_documents(data, path, params, body, false).await } @@ -215,7 +217,7 @@ async fn update_documents( path: web::Path, params: web::Query, body: web::Json>, -) -> Result { +) -> Result { update_multiple_documents(data, path, params, body, true).await } @@ -227,11 +229,11 @@ async fn delete_documents( data: web::Data, path: web::Path, body: web::Json>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let mut writer = data.db.update_write_txn()?; @@ -253,11 +255,11 @@ async fn delete_documents( async fn clear_all_documents( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let mut writer = data.db.update_write_txn()?; diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index 826e8ee02..316b6dd3a 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -3,7 +3,7 @@ use actix_web_macros::{get, put}; use heed::types::{Str, Unit}; use serde::Deserialize; -use crate::error::ResponseError; +use crate::error::Error; use crate::helpers::Authentication; use crate::Data; @@ -14,19 +14,19 @@ pub fn services(cfg: &mut web::ServiceConfig) { } #[get("/health", wrap = "Authentication::Private")] -async fn get_health(data: web::Data) -> Result { +async fn get_health(data: web::Data) -> Result { let reader = data.db.main_read_txn()?; let common_store = data.db.common_store(); if let Ok(Some(_)) = common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY) { - return Err(ResponseError::Maintenance); + return Err(Error::Maintenance); } Ok(HttpResponse::Ok().finish()) } -async fn set_healthy(data: web::Data) -> Result { +async fn set_healthy(data: web::Data) -> Result { let mut writer = data.db.main_write_txn()?; let common_store = data.db.common_store(); common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY)?; @@ -35,7 +35,7 @@ async fn set_healthy(data: web::Data) -> Result) -> Result { +async fn set_unhealthy(data: web::Data) -> Result { let mut writer = data.db.main_write_txn()?; let common_store = data.db.common_store(); common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &())?; @@ -53,7 +53,7 @@ struct HealthBody { async fn change_healthyness( data: web::Data, body: web::Json, -) -> Result { +) -> Result { if body.health { set_healthy(data).await } else { diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index d43967bdd..d669f2d84 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -5,7 +5,7 @@ use log::error; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; -use crate::error::ResponseError; +use crate::error::Error; use crate::helpers::Authentication; use crate::routes::IndexParam; use crate::Data; @@ -40,7 +40,7 @@ struct IndexResponse { } #[get("/indexes", wrap = "Authentication::Private")] -async fn list_indexes(data: web::Data) -> Result { +async fn list_indexes(data: web::Data) -> Result { let reader = data.db.main_read_txn()?; let mut response = Vec::new(); @@ -50,19 +50,19 @@ async fn list_indexes(data: web::Data) -> Result { - let name = index.main.name(&reader)?.ok_or(ResponseError::internal( + let name = index.main.name(&reader)?.ok_or(Error::internal( "Impossible to get the name of an index", ))?; let created_at = index .main .created_at(&reader)? - .ok_or(ResponseError::internal( + .ok_or(Error::internal( "Impossible to get the create date of an index", ))?; let updated_at = index .main .updated_at(&reader)? - .ok_or(ResponseError::internal( + .ok_or(Error::internal( "Impossible to get the last update date of an index", ))?; @@ -97,27 +97,27 @@ async fn list_indexes(data: web::Data) -> Result, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; - let name = index.main.name(&reader)?.ok_or(ResponseError::internal( + let name = index.main.name(&reader)?.ok_or(Error::internal( "Impossible to get the name of an index", ))?; let created_at = index .main .created_at(&reader)? - .ok_or(ResponseError::internal( + .ok_or(Error::internal( "Impossible to get the create date of an index", ))?; let updated_at = index .main .updated_at(&reader)? - .ok_or(ResponseError::internal( + .ok_or(Error::internal( "Impossible to get the last update date of an index", ))?; @@ -150,9 +150,9 @@ struct IndexCreateRequest { async fn create_index( data: web::Data, body: web::Json, -) -> Result { +) -> Result { if let (None, None) = (body.name.clone(), body.uid.clone()) { - return Err(ResponseError::bad_request( + return Err(Error::bad_request( "Index creation must have an uid", )); } @@ -165,7 +165,7 @@ async fn create_index( { uid.to_owned() } else { - return Err(ResponseError::InvalidIndexUid); + return Err(Error::InvalidIndexUid); } } None => loop { @@ -179,7 +179,7 @@ async fn create_index( let created_index = data .db .create_index(&uid) - .map_err(ResponseError::create_index)?; + .map_err(Error::create_index)?; let mut writer = data.db.main_write_txn()?; @@ -189,18 +189,18 @@ async fn create_index( let created_at = created_index .main .created_at(&writer)? - .ok_or(ResponseError::internal("Impossible to read created at"))?; + .ok_or(Error::internal("Impossible to read created at"))?; let updated_at = created_index .main .updated_at(&writer)? - .ok_or(ResponseError::internal("Impossible to read updated at"))?; + .ok_or(Error::internal("Impossible to read updated at"))?; if let Some(id) = body.primary_key.clone() { if let Some(mut schema) = created_index.main.schema(&writer)? { schema .set_primary_key(&id) - .map_err(ResponseError::bad_request)?; + .map_err(Error::bad_request)?; created_index.main.put_schema(&mut writer, &schema)?; } } @@ -238,11 +238,11 @@ async fn update_index( data: web::Data, path: web::Path, body: web::Json, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let mut writer = data.db.main_write_txn()?; @@ -254,7 +254,7 @@ async fn update_index( if let Some(mut schema) = index.main.schema(&writer)? { match schema.primary_key() { Some(_) => { - return Err(ResponseError::bad_request( + return Err(Error::bad_request( "The primary key cannot be updated", )); } @@ -271,19 +271,19 @@ async fn update_index( let reader = data.db.main_read_txn()?; - let name = index.main.name(&reader)?.ok_or(ResponseError::internal( + let name = index.main.name(&reader)?.ok_or(Error::internal( "Impossible to get the name of an index", ))?; let created_at = index .main .created_at(&reader)? - .ok_or(ResponseError::internal( + .ok_or(Error::internal( "Impossible to get the create date of an index", ))?; let updated_at = index .main .updated_at(&reader)? - .ok_or(ResponseError::internal( + .ok_or(Error::internal( "Impossible to get the last update date of an index", ))?; @@ -308,7 +308,7 @@ async fn update_index( async fn delete_index( data: web::Data, path: web::Path, -) -> Result { +) -> Result { data.db.delete_index(&path.index_uid)?; Ok(HttpResponse::NoContent().finish()) @@ -327,11 +327,11 @@ struct UpdateParam { async fn get_update_status( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.update_read_txn()?; @@ -339,7 +339,7 @@ async fn get_update_status( match status { Some(status) => Ok(HttpResponse::Ok().json(status)), - None => Err(ResponseError::NotFound(format!( + None => Err(Error::NotFound(format!( "Update {} not found", path.update_id ))), @@ -350,11 +350,11 @@ async fn get_update_status( async fn get_all_updates_status( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.update_read_txn()?; diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 2bc7b45d0..4224edf79 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -7,7 +7,7 @@ use actix_web_macros::get; use serde::Deserialize; use serde_json::Value; -use crate::error::{ResponseError, FacetCountError}; +use crate::error::{Error, FacetCountError}; use crate::helpers::meilisearch::IndexSearchExt; use crate::helpers::Authentication; use crate::routes::IndexParam; @@ -41,18 +41,18 @@ async fn search_with_url_query( data: web::Data, path: web::Path, params: web::Query, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let schema = index .main .schema(&reader)? - .ok_or(ResponseError::internal("Impossible to retrieve the schema"))?; + .ok_or(Error::internal("Impossible to retrieve the schema"))?; let mut search_builder = index.new_search(params.q.clone()); @@ -90,7 +90,7 @@ async fn search_with_url_query( if let Some(ref facet_filters) = params.facet_filters { match index.main.attributes_for_faceting(&reader)? { Some(ref attrs) => { search_builder.add_facet_filters(FacetFilter::from_str(facet_filters, &schema, attrs)?); }, - None => return Err(ResponseError::FacetExpression("can't filter on facets, as no facet is set".to_string())) + None => return Err(Error::FacetExpression("can't filter on facets, as no facet is set".to_string())) } } diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 04ed8ceed..8d5c48530 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -3,7 +3,7 @@ use actix_web_macros::{delete, get, post}; use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES}; use std::collections::{BTreeMap, BTreeSet, HashSet}; -use crate::error::ResponseError; +use crate::error::Error; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; @@ -33,17 +33,17 @@ async fn update_all( data: web::Data, path: web::Path, body: web::Json, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let mut writer = data.db.update_write_txn()?; let settings = body .into_inner() .into_update() - .map_err(ResponseError::bad_request)?; + .map_err(Error::bad_request)?; let update_id = index.settings_update(&mut writer, settings)?; writer.commit()?; @@ -54,11 +54,11 @@ async fn update_all( async fn get_all( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; @@ -134,11 +134,11 @@ async fn get_all( async fn delete_all( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { @@ -166,11 +166,11 @@ async fn delete_all( async fn get_rules( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let ranking_rules = index @@ -192,11 +192,11 @@ async fn update_rules( data: web::Data, path: web::Path, body: web::Json>>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = Settings { ranking_rules: Some(body.into_inner()), @@ -204,7 +204,7 @@ async fn update_rules( }; let mut writer = data.db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let settings = settings.into_update().map_err(Error::bad_request)?; let update_id = index.settings_update(&mut writer, settings)?; writer.commit()?; @@ -218,11 +218,11 @@ async fn update_rules( async fn delete_rules( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { @@ -244,11 +244,11 @@ async fn delete_rules( async fn get_distinct( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let distinct_attribute = index.main.distinct_attribute(&reader)?; @@ -263,11 +263,11 @@ async fn update_distinct( data: web::Data, path: web::Path, body: web::Json>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = Settings { distinct_attribute: Some(body.into_inner()), @@ -275,7 +275,7 @@ async fn update_distinct( }; let mut writer = data.db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let settings = settings.into_update().map_err(Error::bad_request)?; let update_id = index.settings_update(&mut writer, settings)?; writer.commit()?; @@ -289,11 +289,11 @@ async fn update_distinct( async fn delete_distinct( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { @@ -315,11 +315,11 @@ async fn delete_distinct( async fn get_searchable( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let schema = index.main.schema(&reader)?; let searchable_attributes: Option> = @@ -336,11 +336,11 @@ async fn update_searchable( data: web::Data, path: web::Path, body: web::Json>>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = Settings { searchable_attributes: Some(body.into_inner()), @@ -348,7 +348,7 @@ async fn update_searchable( }; let mut writer = data.db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let settings = settings.into_update().map_err(Error::bad_request)?; let update_id = index.settings_update(&mut writer, settings)?; writer.commit()?; @@ -362,11 +362,11 @@ async fn update_searchable( async fn delete_searchable( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { searchable_attributes: UpdateState::Clear, @@ -387,11 +387,11 @@ async fn delete_searchable( async fn get_displayed( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let schema = index.main.schema(&reader)?; @@ -410,11 +410,11 @@ async fn update_displayed( data: web::Data, path: web::Path, body: web::Json>>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = Settings { displayed_attributes: Some(body.into_inner()), @@ -422,7 +422,7 @@ async fn update_displayed( }; let mut writer = data.db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let settings = settings.into_update().map_err(Error::bad_request)?; let update_id = index.settings_update(&mut writer, settings)?; writer.commit()?; @@ -436,11 +436,11 @@ async fn update_displayed( async fn delete_displayed( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { displayed_attributes: UpdateState::Clear, @@ -461,11 +461,11 @@ async fn delete_displayed( async fn get_accept_new_fields( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let schema = index.main.schema(&reader)?; @@ -483,11 +483,11 @@ async fn update_accept_new_fields( data: web::Data, path: web::Path, body: web::Json>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = Settings { accept_new_fields: Some(body.into_inner()), @@ -495,7 +495,7 @@ async fn update_accept_new_fields( }; let mut writer = data.db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let settings = settings.into_update().map_err(Error::bad_request)?; let update_id = index.settings_update(&mut writer, settings)?; writer.commit()?; diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index 6c31283cd..e7de46b42 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -10,7 +10,7 @@ use serde::Serialize; use sysinfo::{NetworkExt, ProcessExt, ProcessorExt, System, SystemExt}; use walkdir::WalkDir; -use crate::error::ResponseError; +use crate::error::Error; use crate::helpers::Authentication; use crate::routes::IndexParam; use crate::Data; @@ -35,11 +35,11 @@ struct IndexStatsResponse { async fn index_stats( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; @@ -51,7 +51,7 @@ async fn index_stats( let is_indexing = data.is_indexing(&update_reader, &path.index_uid)? - .ok_or(ResponseError::internal( + .ok_or(Error::internal( "Impossible to know if the database is indexing", ))?; @@ -71,7 +71,7 @@ struct StatsResult { } #[get("/stats", wrap = "Authentication::Private")] -async fn get_stats(data: web::Data) -> Result { +async fn get_stats(data: web::Data) -> Result { let mut index_list = HashMap::new(); let reader = data.db.main_read_txn()?; @@ -87,7 +87,7 @@ async fn get_stats(data: web::Data) -> Result let fields_distribution = index.main.fields_distribution(&reader)?.unwrap_or_default(); let is_indexing = data.is_indexing(&update_reader, &index_uid)?.ok_or( - ResponseError::internal("Impossible to know if the database is indexing"), + Error::internal("Impossible to know if the database is indexing"), )?; let response = IndexStatsResponse { diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index 90814e423..f09dd2442 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -3,7 +3,7 @@ use actix_web_macros::{delete, get, post}; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; use std::collections::BTreeSet; -use crate::error::ResponseError; +use crate::error::Error; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; @@ -19,11 +19,11 @@ pub fn services(cfg: &mut web::ServiceConfig) { async fn get( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let stop_words_fst = index.main.stop_words_fst(&reader)?; let stop_words = stop_words_fst.stream().into_strs()?; @@ -39,11 +39,11 @@ async fn update( data: web::Data, path: web::Path, body: web::Json>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { stop_words: UpdateState::Update(body.into_inner()), @@ -64,11 +64,11 @@ async fn update( async fn delete( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { stop_words: UpdateState::Clear, diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index 6c9e77f6d..fd3888e29 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -5,7 +5,7 @@ use actix_web_macros::{delete, get, post}; use indexmap::IndexMap; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use crate::error::ResponseError; +use crate::error::Error; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; @@ -21,11 +21,11 @@ pub fn services(cfg: &mut web::ServiceConfig) { async fn get( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; @@ -51,11 +51,11 @@ async fn update( data: web::Data, path: web::Path, body: web::Json>>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { synonyms: UpdateState::Update(body.into_inner()), @@ -76,11 +76,11 @@ async fn update( async fn delete( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { synonyms: UpdateState::Clear, From d69180ec67e786fd63cfcd2b21f8fabbfa69c3a1 Mon Sep 17 00:00:00 2001 From: mpostma Date: Fri, 22 May 2020 12:03:57 +0200 Subject: [PATCH 03/18] refactor errors / isolate core/http errors --- Cargo.lock | 10 ++ meilisearch-core/Cargo.toml | 1 + meilisearch-core/src/database.rs | 107 ++++++++++-- meilisearch-core/src/error.rs | 17 +- meilisearch-core/src/facets.rs | 19 +-- meilisearch-core/src/query_builder.rs | 2 +- meilisearch-core/src/query_tree.rs | 2 +- .../src/store/documents_fields_counts.rs | 7 +- meilisearch-core/src/store/main.rs | 160 ++++++++++-------- meilisearch-core/src/store/mod.rs | 9 +- meilisearch-core/src/store/synonyms.rs | 13 +- meilisearch-core/src/update/customs_update.rs | 7 +- meilisearch-http/Cargo.toml | 1 + meilisearch-http/src/data.rs | 28 +-- meilisearch-http/src/error.rs | 125 ++++++-------- .../src/helpers/authentication.rs | 8 +- meilisearch-http/src/helpers/meilisearch.rs | 6 +- meilisearch-http/src/lib.rs | 40 +++-- meilisearch-http/src/routes/document.rs | 70 +++----- meilisearch-http/src/routes/health.rs | 41 ++--- meilisearch-http/src/routes/index.rs | 157 +++++++++-------- meilisearch-http/src/routes/search.rs | 16 +- meilisearch-http/src/routes/setting.rs | 144 +++++++++------- meilisearch-http/src/routes/stats.rs | 8 +- meilisearch-http/src/routes/stop_words.rs | 25 +-- meilisearch-http/src/routes/synonym.rs | 31 ++-- meilisearch-http/tests/documents_delete.rs | 1 + meilisearch-schema/Cargo.toml | 1 + meilisearch-schema/src/error.rs | 9 +- 29 files changed, 585 insertions(+), 480 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2b38b19a..dc7454ea6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1653,6 +1653,7 @@ dependencies = [ "jemallocator", "levenshtein_automata", "log", + "meilisearch-error", "meilisearch-schema", "meilisearch-tokenizer", "meilisearch-types", @@ -1673,6 +1674,13 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "meilisearch-error" +version = "0.10.1" +dependencies = [ + "actix-http", +] + [[package]] name = "meilisearch-http" version = "0.10.1" @@ -1699,6 +1707,7 @@ dependencies = [ "log", "main_error", "meilisearch-core", + "meilisearch-error", "meilisearch-schema", "meilisearch-tokenizer", "mime", @@ -1729,6 +1738,7 @@ version = "0.10.1" dependencies = [ "bincode", "indexmap", + "meilisearch-error", "serde", "serde_json", "toml", diff --git a/meilisearch-core/Cargo.toml b/meilisearch-core/Cargo.toml index 912210007..4d092d8af 100644 --- a/meilisearch-core/Cargo.toml +++ b/meilisearch-core/Cargo.toml @@ -24,6 +24,7 @@ intervaltree = "0.2.5" itertools = "0.9.0" levenshtein_automata = { version = "0.2.0", features = ["fst_automaton"] } log = "0.4.8" +meilisearch-error = { path = "../meilisearch-error", version = "0.10.1" } meilisearch-schema = { path = "../meilisearch-schema", version = "0.10.1" } meilisearch-tokenizer = { path = "../meilisearch-tokenizer", version = "0.10.1" } meilisearch-types = { path = "../meilisearch-types", version = "0.10.1" } diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index a26324930..09fc2b326 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -4,17 +4,23 @@ use std::path::Path; use std::sync::{Arc, RwLock}; use std::{fs, thread}; +use chrono::{DateTime, Utc}; use crossbeam_channel::{Receiver, Sender}; -use heed::types::{Str, Unit}; +use heed::types::{Str, Unit, SerdeBincode}; use heed::{CompactionOption, Result as ZResult}; use log::debug; use meilisearch_schema::Schema; -use crate::{store, update, Index, MResult}; +use crate::{store, update, Index, MResult, Error}; pub type BoxUpdateFn = Box; type ArcSwapFn = arc_swap::ArcSwapOption; +type SerdeDatetime = SerdeBincode>; + +const UNHEALTHY_KEY: &str = "_is_unhealthy"; +const LAST_UPDATE_KEY: &str = "last-update"; + pub struct MainT; pub struct UpdateT; @@ -319,20 +325,66 @@ impl Database { self.update_fn.swap(None); } - pub fn main_read_txn(&self) -> heed::Result> { - self.env.typed_read_txn::() + pub fn main_read_txn(&self) -> MResult> { + Ok(self.env.typed_read_txn::()?) } - pub fn main_write_txn(&self) -> heed::Result> { - self.env.typed_write_txn::() + pub(crate) fn main_write_txn(&self) -> MResult> { + Ok(self.env.typed_write_txn::()?) } - pub fn update_read_txn(&self) -> heed::Result> { - self.update_env.typed_read_txn::() + /// Calls f providing it with a writer to the main database. After f is called, makes sure the + /// transaction is commited. Returns whatever result f returns. + pub fn main_write(&self, f: F) -> Result + where + F: FnOnce(&mut heed::RwTxn) -> Result, + E: From, + { + let mut writer = self.main_write_txn()?; + let result = f(&mut writer)?; + writer.commit().map_err(Error::Heed)?; + Ok(result) } - pub fn update_write_txn(&self) -> heed::Result> { - self.update_env.typed_write_txn::() + /// provides a context with a reader to the main database. experimental. + pub fn main_read(&self, f: F) -> Result + where + F: Fn(&heed::RoTxn) -> Result, + E: From, + { + let reader = self.main_read_txn()?; + f(&reader) + } + + pub fn update_read_txn(&self) -> MResult> { + Ok(self.update_env.typed_read_txn::()?) + } + + /// Calls f providing it with a writer to the main database. After f is called, makes sure the + /// transaction is commited. Returns whatever result f returns. + pub fn update_write(&self, f: F) -> Result + where + F: FnOnce(&mut heed::RwTxn) -> Result, + E: From, + { + let mut writer = self.update_write_txn()?; + let result = f(&mut writer)?; + writer.commit().map_err(Error::Heed)?; + Ok(result) + } + + /// provides a context with a reader to the update database. experimental. + pub fn update_read(&self, f: F) -> Result + where + F: Fn(&heed::RoTxn) -> Result, + E: From, + { + let reader = self.update_read_txn()?; + f(&reader) + } + + pub fn update_write_txn(&self) -> MResult> { + Ok(self.update_env.typed_write_txn::()?) } pub fn copy_and_compact_to_path>(&self, path: P) -> ZResult<(File, File)> { @@ -362,9 +414,41 @@ impl Database { indexes.keys().cloned().collect() } - pub fn common_store(&self) -> heed::PolyDatabase { + pub(crate) fn common_store(&self) -> heed::PolyDatabase { self.common_store } + + pub fn last_update(&self, reader: &heed::RoTxn) -> MResult>> { + match self.common_store() + .get::<_, Str, SerdeDatetime>(reader, LAST_UPDATE_KEY)? + { + Some(datetime) => Ok(Some(datetime)), + None => Ok(None), + } + } + + pub fn set_last_update(&self, writer: &mut heed::RwTxn, time: &DateTime) -> MResult<()> { + self.common_store() + .put::<_, Str, SerdeDatetime>(writer, LAST_UPDATE_KEY, time)?; + Ok(()) + } + + pub fn set_healthy(&self, writer: &mut heed::RwTxn) -> MResult<()> { + let common_store = self.common_store(); + common_store.delete::<_, Str>(writer, UNHEALTHY_KEY)?; + Ok(()) + } + + pub fn set_unhealthy(&self, writer: &mut heed::RwTxn) -> MResult<()> { + let common_store = self.common_store(); + common_store.put::<_, Str, Unit>(writer, UNHEALTHY_KEY, &())?; + Ok(()) + } + + pub fn get_health(&self, reader: &heed::RoTxn) -> MResult> { + let common_store = self.common_store(); + Ok(common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY)?) + } } #[cfg(test)] @@ -1094,3 +1178,4 @@ mod tests { assert_matches!(iter.next(), None); } } + diff --git a/meilisearch-core/src/error.rs b/meilisearch-core/src/error.rs index 7990f691f..e01816423 100644 --- a/meilisearch-core/src/error.rs +++ b/meilisearch-core/src/error.rs @@ -9,6 +9,8 @@ pub use fst::Error as FstError; pub use heed::Error as HeedError; pub use pest::error as pest_error; +use meilisearch_error::{ErrorCode, Code}; + pub type MResult = Result; #[derive(Debug)] @@ -21,7 +23,7 @@ pub enum Error { MissingDocumentId, MaxFieldsLimitExceeded, Schema(meilisearch_schema::Error), - Zlmdb(heed::Error), + Heed(heed::Error), Fst(fst::Error), SerdeJson(SerdeJsonError), Bincode(bincode::Error), @@ -32,6 +34,13 @@ pub enum Error { FacetError(FacetError), } +impl ErrorCode for Error { + fn error_code(&self) -> Code { + //TODO populate codes + Code::Other + } +} + impl From for Error { fn from(error: io::Error) -> Error { Error::Io(error) @@ -74,7 +83,7 @@ impl From for Error { impl From for Error { fn from(error: HeedError) -> Error { - Error::Zlmdb(error) + Error::Heed(error) } } @@ -126,7 +135,7 @@ impl fmt::Display for Error { MissingDocumentId => write!(f, "document id is missing"), MaxFieldsLimitExceeded => write!(f, "maximum number of fields in a document exceeded"), Schema(e) => write!(f, "schema error; {}", e), - Zlmdb(e) => write!(f, "heed error; {}", e), + Heed(e) => write!(f, "heed error; {}", e), Fst(e) => write!(f, "fst error; {}", e), SerdeJson(e) => write!(f, "serde json error; {}", e), Bincode(e) => write!(f, "bincode error; {}", e), @@ -174,6 +183,7 @@ pub enum FacetError { AttributeNotFound(String), AttributeNotSet { expected: Vec, found: String }, InvalidDocumentAttribute(String), + NoFacetAttributes, } impl FacetError { @@ -198,6 +208,7 @@ impl fmt::Display for FacetError { AttributeNotFound(attr) => write!(f, "unknown {:?} attribute", attr), AttributeNotSet { found, expected } => write!(f, "`{}` is not set as a faceted attribute. available facet attributes: {}", found, expected.join(", ")), InvalidDocumentAttribute(attr) => write!(f, "invalid document attribute {}, accepted types: String and [String]", attr), + NoFacetAttributes => write!(f, "No attributes are set for faceting"), } } } diff --git a/meilisearch-core/src/facets.rs b/meilisearch-core/src/facets.rs index dc8654915..cc1737eaf 100644 --- a/meilisearch-core/src/facets.rs +++ b/meilisearch-core/src/facets.rs @@ -13,7 +13,7 @@ use meilisearch_schema::{FieldId, Schema}; use meilisearch_types::DocumentId; use crate::database::MainT; -use crate::error::{FacetError, Error}; +use crate::error::{FacetError, MResult}; use crate::store::BEU16; /// Data structure used to represent a boolean expression in the form of nested arrays. @@ -34,14 +34,13 @@ impl FacetFilter { s: &str, schema: &Schema, attributes_for_faceting: &[FieldId], - ) -> Result { - + ) -> MResult { let parsed = serde_json::from_str::(s).map_err(|e| FacetError::ParsingError(e.to_string()))?; let mut filter = Vec::new(); match parsed { Value::Array(and_exprs) => { if and_exprs.is_empty() { - return Err(FacetError::EmptyArray); + return Err(FacetError::EmptyArray.into()); } for expr in and_exprs { match expr { @@ -51,7 +50,7 @@ impl FacetFilter { } Value::Array(or_exprs) => { if or_exprs.is_empty() { - return Err(FacetError::EmptyArray); + return Err(FacetError::EmptyArray.into()); } let mut inner = Vec::new(); for expr in or_exprs { @@ -60,17 +59,17 @@ impl FacetFilter { let key = FacetKey::from_str( &s, schema, attributes_for_faceting)?; inner.push(key); } - bad_value => return Err(FacetError::unexpected_token(&["String"], bad_value)), + bad_value => return Err(FacetError::unexpected_token(&["String"], bad_value).into()), } } filter.push(Either::Left(inner)); } - bad_value => return Err(FacetError::unexpected_token(&["Array", "String"], bad_value)), + bad_value => return Err(FacetError::unexpected_token(&["Array", "String"], bad_value).into()), } } return Ok(Self(filter)); } - bad_value => Err(FacetError::unexpected_token(&["Array"], bad_value)), + bad_value => Err(FacetError::unexpected_token(&["Array"], bad_value).into()), } } } @@ -183,7 +182,7 @@ pub fn facet_map_from_docids( index: &crate::Index, document_ids: &[DocumentId], attributes_for_facetting: &[FieldId], -) -> Result>, Error> { +) -> MResult>> { let mut facet_map = HashMap::new(); for document_id in document_ids { for result in index @@ -210,7 +209,7 @@ pub fn facet_map_from_docs( schema: &Schema, documents: &HashMap>, attributes_for_facetting: &[FieldId], -) -> Result>, Error> { +) -> MResult>> { let mut facet_map = HashMap::new(); let attributes_for_facetting = attributes_for_facetting .iter() diff --git a/meilisearch-core/src/query_builder.rs b/meilisearch-core/src/query_builder.rs index c9ed1933e..72accfcde 100644 --- a/meilisearch-core/src/query_builder.rs +++ b/meilisearch-core/src/query_builder.rs @@ -268,7 +268,7 @@ mod tests { let alternatives = self .index .synonyms - .synonyms(&writer, word.as_bytes()) + .synonyms_fst(&writer, word.as_bytes()) .unwrap(); let new = sdset_into_fstset(&new); diff --git a/meilisearch-core/src/query_tree.rs b/meilisearch-core/src/query_tree.rs index 2687028a0..4a3a622b2 100644 --- a/meilisearch-core/src/query_tree.rs +++ b/meilisearch-core/src/query_tree.rs @@ -147,7 +147,7 @@ fn split_best_frequency<'a>(reader: &heed::RoTxn, ctx: &Context, word: &' fn fetch_synonyms(reader: &heed::RoTxn, ctx: &Context, words: &[&str]) -> MResult>> { let words = normalize_str(&words.join(" ")); - let set = ctx.synonyms.synonyms(reader, words.as_bytes())?; + let set = ctx.synonyms.synonyms_fst(reader, words.as_bytes())?; let mut strings = Vec::new(); let mut stream = set.stream(); diff --git a/meilisearch-core/src/store/documents_fields_counts.rs b/meilisearch-core/src/store/documents_fields_counts.rs index 69bacd5f7..f0d23c99b 100644 --- a/meilisearch-core/src/store/documents_fields_counts.rs +++ b/meilisearch-core/src/store/documents_fields_counts.rs @@ -4,6 +4,7 @@ use crate::DocumentId; use heed::types::OwnedType; use heed::Result as ZResult; use meilisearch_schema::IndexedPos; +use crate::MResult; #[derive(Copy, Clone)] pub struct DocumentsFieldsCounts { @@ -60,7 +61,7 @@ impl DocumentsFieldsCounts { Ok(DocumentFieldsCountsIter { iter }) } - pub fn documents_ids<'txn>(self, reader: &'txn heed::RoTxn) -> ZResult> { + pub fn documents_ids<'txn>(self, reader: &'txn heed::RoTxn) -> MResult> { let iter = self.documents_fields_counts.iter(reader)?; Ok(DocumentsIdsIter { last_seen_id: None, @@ -102,7 +103,7 @@ pub struct DocumentsIdsIter<'txn> { } impl Iterator for DocumentsIdsIter<'_> { - type Item = ZResult; + type Item = MResult; fn next(&mut self) -> Option { for result in &mut self.iter { @@ -114,7 +115,7 @@ impl Iterator for DocumentsIdsIter<'_> { return Some(Ok(document_id)); } } - Err(e) => return Some(Err(e)), + Err(e) => return Some(Err(e.into())), } } None diff --git a/meilisearch-core/src/store/main.rs b/meilisearch-core/src/store/main.rs index 864970320..7873b3dba 100644 --- a/meilisearch-core/src/store/main.rs +++ b/meilisearch-core/src/store/main.rs @@ -2,14 +2,13 @@ use std::borrow::Cow; use std::collections::HashMap; use chrono::{DateTime, Utc}; -use heed::Result as ZResult; use heed::types::{ByteSlice, OwnedType, SerdeBincode, Str}; use meilisearch_schema::{FieldId, Schema}; use meilisearch_types::DocumentId; use sdset::Set; use crate::database::MainT; -use crate::RankedMap; +use crate::{RankedMap, MResult}; use crate::settings::RankingRule; use crate::{FstSetCow, FstMapCow}; use super::{CowSet, DocumentsIds}; @@ -41,39 +40,38 @@ pub struct Main { } impl Main { - pub fn clear(self, writer: &mut heed::RwTxn) -> ZResult<()> { - self.main.clear(writer) + pub fn clear(self, writer: &mut heed::RwTxn) -> MResult<()> { + Ok(self.main.clear(writer)?) } - pub fn put_name(self, writer: &mut heed::RwTxn, name: &str) -> ZResult<()> { - self.main.put::<_, Str, Str>(writer, NAME_KEY, name) + pub fn put_name(self, writer: &mut heed::RwTxn, name: &str) -> MResult<()> { + Ok(self.main.put::<_, Str, Str>(writer, NAME_KEY, name)?) } - pub fn name(self, reader: &heed::RoTxn) -> ZResult> { + pub fn name(self, reader: &heed::RoTxn) -> MResult> { Ok(self .main .get::<_, Str, Str>(reader, NAME_KEY)? .map(|name| name.to_owned())) } - pub fn put_created_at(self, writer: &mut heed::RwTxn) -> ZResult<()> { - self.main - .put::<_, Str, SerdeDatetime>(writer, CREATED_AT_KEY, &Utc::now()) + pub fn put_created_at(self, writer: &mut heed::RwTxn) -> MResult<()> { + Ok(self.main.put::<_, Str, SerdeDatetime>(writer, CREATED_AT_KEY, &Utc::now())?) } - pub fn created_at(self, reader: &heed::RoTxn) -> ZResult>> { - self.main.get::<_, Str, SerdeDatetime>(reader, CREATED_AT_KEY) + pub fn created_at(self, reader: &heed::RoTxn) -> MResult>> { + Ok(self.main.get::<_, Str, SerdeDatetime>(reader, CREATED_AT_KEY)?) } - pub fn put_updated_at(self, writer: &mut heed::RwTxn) -> ZResult<()> { - self.main - .put::<_, Str, SerdeDatetime>(writer, UPDATED_AT_KEY, &Utc::now()) + pub fn put_updated_at(self, writer: &mut heed::RwTxn) -> MResult<()> { + Ok(self.main.put::<_, Str, SerdeDatetime>(writer, UPDATED_AT_KEY, &Utc::now())?) } - pub fn updated_at(self, reader: &heed::RoTxn) -> ZResult>> { - self.main.get::<_, Str, SerdeDatetime>(reader, UPDATED_AT_KEY) + pub fn updated_at(self, reader: &heed::RoTxn) -> MResult>> { + Ok(self.main.get::<_, Str, SerdeDatetime>(reader, UPDATED_AT_KEY)?) } +<<<<<<< HEAD pub fn put_internal_docids(self, writer: &mut heed::RwTxn, ids: &sdset::Set) -> ZResult<()> { self.main.put::<_, Str, DocumentsIds>(writer, INTERNAL_DOCIDS_KEY, ids) } @@ -120,6 +118,21 @@ impl Main { let mut build = fst::MapBuilder::memory(); while let Some((docid, values)) = op.next() { build.insert(docid, values[0].value).unwrap(); +======= + pub fn put_words_fst(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> MResult<()> { + let bytes = fst.as_fst().as_bytes(); + Ok(self.main.put::<_, Str, ByteSlice>(writer, WORDS_KEY, bytes)?) + } + + pub unsafe fn static_words_fst(self, reader: &heed::RoTxn) -> MResult> { + match self.main.get::<_, Str, ByteSlice>(reader, WORDS_KEY)? { + Some(bytes) => { + let bytes: &'static [u8] = std::mem::transmute(bytes); + let set = fst::Set::from_static_slice(bytes).unwrap(); + Ok(Some(set)) + } + None => Ok(None), +>>>>>>> 5c760d3... refactor errors / isolate core/http errors } drop(op); @@ -161,58 +174,76 @@ impl Main { self.main.put::<_, Str, ByteSlice>(writer, WORDS_KEY, fst.as_fst().as_bytes()) } - pub fn words_fst(self, reader: &heed::RoTxn) -> ZResult { + pub fn words_fst(self, reader: &heed::RoTxn) -> MResult> { match self.main.get::<_, Str, ByteSlice>(reader, WORDS_KEY)? { Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()), None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()), } } - pub fn put_schema(self, writer: &mut heed::RwTxn, schema: &Schema) -> ZResult<()> { - self.main.put::<_, Str, SerdeBincode>(writer, SCHEMA_KEY, schema) + pub fn put_schema(self, writer: &mut heed::RwTxn, schema: &Schema) -> MResult<()> { + Ok(self.main.put::<_, Str, SerdeBincode>(writer, SCHEMA_KEY, schema)?) } - pub fn schema(self, reader: &heed::RoTxn) -> ZResult> { - self.main.get::<_, Str, SerdeBincode>(reader, SCHEMA_KEY) + pub fn schema(self, reader: &heed::RoTxn) -> MResult> { + Ok(self.main.get::<_, Str, SerdeBincode>(reader, SCHEMA_KEY)?) } - pub fn delete_schema(self, writer: &mut heed::RwTxn) -> ZResult { - self.main.delete::<_, Str>(writer, SCHEMA_KEY) + pub fn delete_schema(self, writer: &mut heed::RwTxn) -> MResult { + Ok(self.main.delete::<_, Str>(writer, SCHEMA_KEY)?) } - pub fn put_ranked_map(self, writer: &mut heed::RwTxn, ranked_map: &RankedMap) -> ZResult<()> { - self.main.put::<_, Str, SerdeBincode>(writer, RANKED_MAP_KEY, &ranked_map) + pub fn put_ranked_map(self, writer: &mut heed::RwTxn, ranked_map: &RankedMap) -> MResult<()> { + Ok(self.main.put::<_, Str, SerdeBincode>(writer, RANKED_MAP_KEY, &ranked_map)?) } - pub fn ranked_map(self, reader: &heed::RoTxn) -> ZResult> { - self.main.get::<_, Str, SerdeBincode>(reader, RANKED_MAP_KEY) + pub fn ranked_map(self, reader: &heed::RoTxn) -> MResult> { + Ok(self.main.get::<_, Str, SerdeBincode>(reader, RANKED_MAP_KEY)?) } - pub fn put_synonyms_fst>(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> ZResult<()> { + pub fn put_synonyms_fst>(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> MResult<()> { let bytes = fst.as_fst().as_bytes(); - self.main.put::<_, Str, ByteSlice>(writer, SYNONYMS_KEY, bytes) + Ok(self.main.put::<_, Str, ByteSlice>(writer, SYNONYMS_KEY, bytes)?) } - pub fn synonyms_fst(self, reader: &heed::RoTxn) -> ZResult { + pub(crate) fn synonyms_fst(self, reader: &heed::RoTxn) -> MResult> { match self.main.get::<_, Str, ByteSlice>(reader, SYNONYMS_KEY)? { Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()), None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()), } } - pub fn put_stop_words_fst>(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> ZResult<()> { - let bytes = fst.as_fst().as_bytes(); - self.main.put::<_, Str, ByteSlice>(writer, STOP_WORDS_KEY, bytes) + pub fn synonyms_list(self, reader: &heed::RoTxn) -> MResult> { + let synonyms = self + .synonyms_fst(&reader)? + .unwrap_or_default() + .stream() + .into_strs()?; + Ok(synonyms) } - pub fn stop_words_fst(self, reader: &heed::RoTxn) -> ZResult { + pub fn put_stop_words_fst>(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> MResult<()> { + let bytes = fst.as_fst().as_bytes(); + Ok(self.main.put::<_, Str, ByteSlice>(writer, STOP_WORDS_KEY, bytes)?) + } + + pub(crate) fn stop_words_fst(self, reader: &heed::RoTxn) -> MResult { match self.main.get::<_, Str, ByteSlice>(reader, STOP_WORDS_KEY)? { Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()), None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()), } } - pub fn put_number_of_documents(self, writer: &mut heed::RwTxn, f: F) -> ZResult + pub fn stop_words_list(self, reader: &heed::RoTxn) -> MResult> { + let stop_word_list = self + .stop_words_fst(reader)? + .unwrap_or_default() + .stream() + .into_strs()?; + Ok(stop_word_list) + } + + pub fn put_number_of_documents(self, writer: &mut heed::RwTxn, f: F) -> MResult where F: Fn(u64) -> u64, { @@ -222,11 +253,10 @@ impl Main { Ok(new) } - pub fn number_of_documents(self, reader: &heed::RoTxn) -> ZResult { + pub fn number_of_documents(self, reader: &heed::RoTxn) -> MResult { match self .main - .get::<_, Str, OwnedType>(reader, NUMBER_OF_DOCUMENTS_KEY)? - { + .get::<_, Str, OwnedType>(reader, NUMBER_OF_DOCUMENTS_KEY)? { Some(value) => Ok(value), None => Ok(0), } @@ -235,13 +265,12 @@ impl Main { pub fn put_fields_distribution( self, writer: &mut heed::RwTxn, - fields_distribution: &FreqsMap, - ) -> ZResult<()> { - self.main - .put::<_, Str, SerdeFreqsMap>(writer, FIELDS_DISTRIBUTION_KEY, fields_distribution) + fields_frequency: &FreqsMap, + ) -> MResult<()> { + Ok(self.main.put::<_, Str, SerdeFreqsMap>(writer, FIELDS_FREQUENCY_KEY, fields_frequency)?) } - pub fn fields_distribution(&self, reader: &heed::RoTxn) -> ZResult> { + pub fn fields_distribution(&self, reader: &heed::RoTxn) -> MResult> { match self .main .get::<_, Str, SerdeFreqsMap>(reader, FIELDS_DISTRIBUTION_KEY)? @@ -251,51 +280,50 @@ impl Main { } } - pub fn attributes_for_faceting<'txn>(&self, reader: &'txn heed::RoTxn) -> ZResult>>> { - self.main.get::<_, Str, CowSet>(reader, ATTRIBUTES_FOR_FACETING_KEY) + pub fn attributes_for_faceting<'txn>(&self, reader: &'txn heed::RoTxn) -> MResult>>> { + Ok(self.main.get::<_, Str, CowSet>(reader, ATTRIBUTES_FOR_FACETING_KEY)?) } - pub fn put_attributes_for_faceting(self, writer: &mut heed::RwTxn, attributes: &Set) -> ZResult<()> { - self.main.put::<_, Str, CowSet>(writer, ATTRIBUTES_FOR_FACETING_KEY, attributes) + pub fn put_attributes_for_faceting(self, writer: &mut heed::RwTxn, attributes: &Set) -> MResult<()> { + Ok(self.main.put::<_, Str, CowSet>(writer, ATTRIBUTES_FOR_FACETING_KEY, attributes)?) } - pub fn delete_attributes_for_faceting(self, writer: &mut heed::RwTxn) -> ZResult { - self.main.delete::<_, Str>(writer, ATTRIBUTES_FOR_FACETING_KEY) + pub fn delete_attributes_for_faceting(self, writer: &mut heed::RwTxn) -> MResult { + Ok(self.main.delete::<_, Str>(writer, ATTRIBUTES_FOR_FACETING_KEY)?) } - pub fn ranking_rules(&self, reader: &heed::RoTxn) -> ZResult>> { - self.main.get::<_, Str, SerdeBincode>>(reader, RANKING_RULES_KEY) + pub fn ranking_rules(&self, reader: &heed::RoTxn) -> MResult>> { + Ok(self.main.get::<_, Str, SerdeBincode>>(reader, RANKING_RULES_KEY)?) } - pub fn put_ranking_rules(self, writer: &mut heed::RwTxn, value: &[RankingRule]) -> ZResult<()> { - self.main.put::<_, Str, SerdeBincode>>(writer, RANKING_RULES_KEY, &value.to_vec()) + pub fn put_ranking_rules(self, writer: &mut heed::RwTxn, value: &[RankingRule]) -> MResult<()> { + Ok(self.main.put::<_, Str, SerdeBincode>>(writer, RANKING_RULES_KEY, &value.to_vec())?) } - pub fn delete_ranking_rules(self, writer: &mut heed::RwTxn) -> ZResult { - self.main.delete::<_, Str>(writer, RANKING_RULES_KEY) + pub fn delete_ranking_rules(self, writer: &mut heed::RwTxn) -> MResult { + Ok(self.main.delete::<_, Str>(writer, RANKING_RULES_KEY)?) } - pub fn distinct_attribute(&self, reader: &heed::RoTxn) -> ZResult> { + pub fn distinct_attribute(&self, reader: &heed::RoTxn) -> MResult> { if let Some(value) = self.main.get::<_, Str, Str>(reader, DISTINCT_ATTRIBUTE_KEY)? { return Ok(Some(value.to_owned())) } return Ok(None) } - pub fn put_distinct_attribute(self, writer: &mut heed::RwTxn, value: &str) -> ZResult<()> { - self.main.put::<_, Str, Str>(writer, DISTINCT_ATTRIBUTE_KEY, value) + pub fn put_distinct_attribute(self, writer: &mut heed::RwTxn, value: &str) -> MResult<()> { + Ok(self.main.put::<_, Str, Str>(writer, DISTINCT_ATTRIBUTE_KEY, value)?) } - pub fn delete_distinct_attribute(self, writer: &mut heed::RwTxn) -> ZResult { - self.main.delete::<_, Str>(writer, DISTINCT_ATTRIBUTE_KEY) + pub fn delete_distinct_attribute(self, writer: &mut heed::RwTxn) -> MResult { + Ok(self.main.delete::<_, Str>(writer, DISTINCT_ATTRIBUTE_KEY)?) } - pub fn put_customs(self, writer: &mut heed::RwTxn, customs: &[u8]) -> ZResult<()> { - self.main - .put::<_, Str, ByteSlice>(writer, CUSTOMS_KEY, customs) + pub fn put_customs(self, writer: &mut heed::RwTxn, customs: &[u8]) -> MResult<()> { + Ok(self.main.put::<_, Str, ByteSlice>(writer, CUSTOMS_KEY, customs)?) } - pub fn customs<'txn>(self, reader: &'txn heed::RoTxn) -> ZResult> { - self.main.get::<_, Str, ByteSlice>(reader, CUSTOMS_KEY) + pub fn customs<'txn>(self, reader: &'txn heed::RoTxn) -> MResult> { + Ok(self.main.get::<_, Str, ByteSlice>(reader, CUSTOMS_KEY)?) } } diff --git a/meilisearch-core/src/store/mod.rs b/meilisearch-core/src/store/mod.rs index d19d3f7d3..fa5baa831 100644 --- a/meilisearch-core/src/store/mod.rs +++ b/meilisearch-core/src/store/mod.rs @@ -31,7 +31,6 @@ use std::collections::HashSet; use std::convert::TryInto; use std::{mem, ptr}; -use heed::Result as ZResult; use heed::{BytesEncode, BytesDecode}; use meilisearch_schema::{IndexedPos, FieldId}; use sdset::{Set, SetBuf}; @@ -279,14 +278,14 @@ impl Index { } } - pub fn customs_update(&self, writer: &mut heed::RwTxn, customs: Vec) -> ZResult { + pub fn customs_update(&self, writer: &mut heed::RwTxn, customs: Vec) -> MResult { let _ = self.updates_notifier.send(UpdateEvent::NewUpdate); - update::push_customs_update(writer, self.updates, self.updates_results, customs) + Ok(update::push_customs_update(writer, self.updates, self.updates_results, customs)?) } - pub fn settings_update(&self, writer: &mut heed::RwTxn, update: SettingsUpdate) -> ZResult { + pub fn settings_update(&self, writer: &mut heed::RwTxn, update: SettingsUpdate) -> MResult { let _ = self.updates_notifier.send(UpdateEvent::NewUpdate); - update::push_settings_update(writer, self.updates, self.updates_results, update) + Ok(update::push_settings_update(writer, self.updates, self.updates_results, update)?) } pub fn documents_addition(&self) -> update::DocumentsAddition { diff --git a/meilisearch-core/src/store/synonyms.rs b/meilisearch-core/src/store/synonyms.rs index 1ec8d313c..cf7cf5811 100644 --- a/meilisearch-core/src/store/synonyms.rs +++ b/meilisearch-core/src/store/synonyms.rs @@ -4,7 +4,7 @@ use heed::Result as ZResult; use heed::types::ByteSlice; use crate::database::MainT; -use crate::FstSetCow; +use crate::{FstSetCow, MResult}; #[derive(Copy, Clone)] pub struct Synonyms { @@ -27,10 +27,19 @@ impl Synonyms { self.synonyms.clear(writer) } - pub fn synonyms<'txn>(self, reader: &'txn heed::RoTxn, word: &[u8]) -> ZResult> { + pub fn synonyms_fst<'txn>(self, reader: &'txn heed::RoTxn, word: &[u8]) -> ZResult> { match self.synonyms.get(reader, word)? { Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()), None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()), } } + + pub fn synonyms(self, reader: &heed::RoTxn, word: &[u8]) -> MResult>> { + let synonyms = self + .synonyms_fst(&reader, word)? + .map(|list| list.stream().into_strs()) + .transpose()?; + Ok(synonyms) + } } + diff --git a/meilisearch-core/src/update/customs_update.rs b/meilisearch-core/src/update/customs_update.rs index a413d13a6..a3a66e61d 100644 --- a/meilisearch-core/src/update/customs_update.rs +++ b/meilisearch-core/src/update/customs_update.rs @@ -1,14 +1,13 @@ -use heed::Result as ZResult; use crate::database::{MainT, UpdateT}; -use crate::store; +use crate::{store, MResult}; use crate::update::{next_update_id, Update}; pub fn apply_customs_update( writer: &mut heed::RwTxn, main_store: store::Main, customs: &[u8], -) -> ZResult<()> { +) -> MResult<()> { main_store.put_customs(writer, customs) } @@ -17,7 +16,7 @@ pub fn push_customs_update( updates_store: store::Updates, updates_results_store: store::UpdatesResults, customs: Vec, -) -> ZResult { +) -> MResult { let last_update_id = next_update_id(writer, updates_store, updates_results_store)?; let update = Update::customs(customs); diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index 368c8f649..20f4d4f4e 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -35,6 +35,7 @@ indexmap = { version = "1.3.2", features = ["serde-1"] } log = "0.4.8" main_error = "0.1.0" meilisearch-core = { path = "../meilisearch-core", version = "0.10.1" } +meilisearch-error = { path = "../meilisearch-error", version = "0.10.1" } meilisearch-schema = { path = "../meilisearch-schema", version = "0.10.1" } meilisearch-tokenizer = {path = "../meilisearch-tokenizer", version = "0.10.1"} mime = "0.3.16" diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index 39b7f9b1e..e278f0ffc 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -2,20 +2,14 @@ use std::collections::HashMap; use std::ops::Deref; use std::sync::Arc; -use chrono::{DateTime, Utc}; -use heed::types::{SerdeBincode, Str}; use log::error; -use meilisearch_core::{Database, DatabaseOptions, Error as MError, MResult, MainT, UpdateT}; +use meilisearch_core::{Database, DatabaseOptions, MResult, MainT, UpdateT}; use sha2::Digest; use sysinfo::Pid; use crate::index_update_callback; use crate::option::Opt; -const LAST_UPDATE_KEY: &str = "last-update"; - -type SerdeDatetime = SerdeBincode>; - #[derive(Clone)] pub struct Data { inner: Arc, @@ -70,24 +64,6 @@ impl DataInner { } } - pub fn last_update(&self, reader: &heed::RoTxn) -> MResult>> { - match self - .db - .common_store() - .get::<_, Str, SerdeDatetime>(reader, LAST_UPDATE_KEY)? - { - Some(datetime) => Ok(Some(datetime)), - None => Ok(None), - } - } - - pub fn set_last_update(&self, writer: &mut heed::RwTxn) -> MResult<()> { - self.db - .common_store() - .put::<_, Str, SerdeDatetime>(writer, LAST_UPDATE_KEY, &Utc::now()) - .map_err(Into::into) - } - pub fn compute_stats(&self, writer: &mut heed::RwTxn, index_uid: &str) -> MResult<()> { let index = match self.db.open_index(&index_uid) { Some(index) => index, @@ -124,7 +100,6 @@ impl DataInner { index .main .put_fields_distribution(writer, &distribution) - .map_err(MError::Zlmdb) } } @@ -170,3 +145,4 @@ impl Data { data } } + diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index e59848d3d..b5dde6f6e 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::error; use actix_http::ResponseBuilder; use actix_web as aweb; @@ -6,6 +7,25 @@ use actix_web::http::StatusCode; use serde_json::json; use actix_web::error::JsonPayloadError; +use meilisearch_error::{ErrorCode, Code}; + +#[derive(Debug)] +pub struct ResponseError { + inner: Box, +} + +impl fmt::Display for ResponseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl From for ResponseError { + fn from(error: Error) -> ResponseError { + ResponseError { inner: Box::new(error) } + } +} + #[derive(Debug)] pub enum Error { BadParameter(String, String), @@ -26,10 +46,18 @@ pub enum Error { SearchDocuments(String), PayloadTooLarge, UnsupportedMediaType, - FacetExpression(String), - FacetCount(String), } +impl error::Error for Error {} + +impl ErrorCode for Error { + fn error_code(&self) -> Code { + //TODO populate with right error codes + Code::Other + } +} + +#[derive(Debug)] pub enum FacetCountError { AttributeNotSet(String), SyntaxError(String), @@ -37,6 +65,14 @@ pub enum FacetCountError { NoFacetSet, } +impl error::Error for FacetCountError {} + +impl ErrorCode for FacetCountError { + fn error_code(&self) -> Code { + unimplemented!() + } +} + impl FacetCountError { pub fn unexpected_token(found: impl ToString, expected: &'static [&'static str]) -> FacetCountError { let found = found.to_string(); @@ -137,94 +173,42 @@ impl fmt::Display for Error { Self::InvalidIndexUid => f.write_str("Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."), Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err), Self::Maintenance => f.write_str("Server is in maintenance, please try again later"), - Self::FilterParsing(err) => write!(f, "parsing error: {}", err), Self::MissingAuthorizationHeader => f.write_str("You must have an authorization token"), Self::MissingHeader(header) => write!(f, "Header {} is missing", header), Self::NotFound(err) => write!(f, "{} not found", err), Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err), Self::RetrieveDocument(id, err) => write!(f, "impossible to retrieve the document with id: {}; {}", id, err), Self::SearchDocuments(err) => write!(f, "impossible to search documents; {}", err), - Self::FacetExpression(e) => write!(f, "error parsing facet filter expression: {}", e), Self::PayloadTooLarge => f.write_str("Payload to large"), Self::UnsupportedMediaType => f.write_str("Unsupported media type"), - Self::FacetCount(e) => write!(f, "error with facet count: {}", e), } } } -impl aweb::error::ResponseError for Error { +impl aweb::error::ResponseError for ResponseError { fn error_response(&self) -> aweb::HttpResponse { + let error_code = self.inner.error_code().internal(); ResponseBuilder::new(self.status_code()).json(json!({ "message": self.to_string(), + "errorCode": error_code, + "errorLink": format!("docs.meilisearch.come/error/{}", error_code), })) } fn status_code(&self) -> StatusCode { - match *self { - Self::BadParameter(_, _) - | Self::BadRequest(_) - | Self::CreateIndex(_) - | Self::InvalidIndexUid - | Self::OpenIndex(_) - | Self::RetrieveDocument(_, _) - | Self::FacetExpression(_) - | Self::SearchDocuments(_) - | Self::FacetCount(_) - | Self::FilterParsing(_) => StatusCode::BAD_REQUEST, - Self::DocumentNotFound(_) - | Self::IndexNotFound(_) - | Self::NotFound(_) => StatusCode::NOT_FOUND, - Self::InvalidToken(_) - | Self::MissingHeader(_) => StatusCode::UNAUTHORIZED, - Self::MissingAuthorizationHeader => StatusCode::FORBIDDEN, - Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, - Self::Maintenance => StatusCode::SERVICE_UNAVAILABLE, - Self::PayloadTooLarge => StatusCode::PAYLOAD_TOO_LARGE, - Self::UnsupportedMediaType => StatusCode::UNSUPPORTED_MEDIA_TYPE, - } + self.inner.error_code().http() } } -impl From for Error { - fn from(err: meilisearch_core::HeedError) -> Error { - Error::Internal(err.to_string()) +impl From for ResponseError { + fn from(err: meilisearch_core::Error) -> ResponseError { + ResponseError { inner: Box::new(err) } } } -impl From for Error { - fn from(err: meilisearch_core::FstError) -> Error { - Error::Internal(err.to_string()) - } -} - -impl From for Error { - fn from(error: meilisearch_core::FacetError) -> Error { - Error::FacetExpression(error.to_string()) - } -} - -impl From for Error { - fn from(err: meilisearch_core::Error) -> Error { - use meilisearch_core::pest_error::LineColLocation::*; - match err { - meilisearch_core::Error::FilterParseError(e) => { - let (line, column) = match e.line_col { - Span((line, _), (column, _)) => (line, column), - Pos((line, column)) => (line, column), - }; - let message = format!("parsing error on line {} at column {}: {}", line, column, e.variant.message()); - - Error::FilterParsing(message) - }, - meilisearch_core::Error::FacetError(e) => Error::FacetExpression(e.to_string()), - _ => Error::Internal(err.to_string()), - } - } -} - -impl From for Error { - fn from(err: meilisearch_schema::Error) -> Error { - Error::Internal(err.to_string()) +impl From for ResponseError { + fn from(err: meilisearch_schema::Error) -> ResponseError { + ResponseError { inner: Box::new(err) } } } @@ -234,9 +218,9 @@ impl From for Error { } } -impl From for Error { - fn from(other: FacetCountError) -> Error { - Error::FacetCount(other.to_string()) +impl From for ResponseError { + fn from(err: FacetCountError) -> ResponseError { + ResponseError { inner: Box::new(err) } } } @@ -251,6 +235,7 @@ impl From for Error { } } -pub fn json_error_handler(err: JsonPayloadError) -> Error { - err.into() +pub fn json_error_handler(err: JsonPayloadError) -> ResponseError { + let error = Error::from(err); + error.into() } diff --git a/meilisearch-http/src/helpers/authentication.rs b/meilisearch-http/src/helpers/authentication.rs index 4b921a783..927665ffe 100644 --- a/meilisearch-http/src/helpers/authentication.rs +++ b/meilisearch-http/src/helpers/authentication.rs @@ -7,7 +7,7 @@ use actix_service::{Service, Transform}; use actix_web::{dev::ServiceRequest, dev::ServiceResponse}; use futures::future::{err, ok, Future, Ready}; -use crate::error::Error; +use crate::error::{Error, ResponseError}; use crate::Data; #[derive(Clone)] @@ -71,10 +71,10 @@ where let auth_header = match req.headers().get("X-Meili-API-Key") { Some(auth) => match auth.to_str() { Ok(auth) => auth, - Err(_) => return Box::pin(err(Error::MissingAuthorizationHeader.into())), + Err(_) => return Box::pin(err(ResponseError::from(Error::MissingAuthorizationHeader).into())), }, None => { - return Box::pin(err(Error::MissingAuthorizationHeader.into())); + return Box::pin(err(ResponseError::from(Error::MissingAuthorizationHeader).into())); } }; @@ -95,7 +95,7 @@ where Box::pin(svc.call(req)) } else { Box::pin(err( - Error::InvalidToken(auth_header.to_string()).into() + ResponseError::from(Error::InvalidToken(auth_header.to_string())).into() )) } } diff --git a/meilisearch-http/src/helpers/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index 297bb1f54..f760bb1ac 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -17,7 +17,7 @@ use serde_json::Value; use siphasher::sip::SipHasher; use slice_group_by::GroupBy; -use crate::error::Error; +use crate::error::{Error, ResponseError}; pub trait IndexSearchExt { fn new_search(&self, query: String) -> SearchBuilder; @@ -107,7 +107,7 @@ impl<'a> SearchBuilder<'a> { self } - pub fn search(self, reader: &heed::RoTxn) -> Result { + pub fn search(self, reader: &heed::RoTxn) -> Result { let schema = self .index .main @@ -260,7 +260,7 @@ impl<'a> SearchBuilder<'a> { reader: &heed::RoTxn, ranked_map: &'a RankedMap, schema: &Schema, - ) -> Result>, Error> { + ) -> Result>, ResponseError> { let ranking_rules = self.index.main.ranking_rules(reader)?; if let Some(ranking_rules) = ranking_rules { diff --git a/meilisearch-http/src/lib.rs b/meilisearch-http/src/lib.rs index e2e24d4d2..5a44d5761 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -7,14 +7,17 @@ pub mod models; pub mod option; pub mod routes; -pub use self::data::Data; -use self::error::json_error_handler; use actix_http::Error; use actix_service::ServiceFactory; use actix_web::{dev, web, App}; +use chrono::Utc; use log::error; + use meilisearch_core::ProcessedUpdateResult; +pub use self::data::Data; +use self::error::{json_error_handler, ResponseError}; + pub fn create_app( data: &Data, ) -> App< @@ -55,28 +58,23 @@ pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpda if let Some(index) = data.db.open_index(&index_uid) { let db = &data.db; - let mut writer = match db.main_write_txn() { - Ok(writer) => writer, - Err(e) => { - error!("Impossible to get write_txn; {}", e); - return; + let res = db.main_write::<_, _, ResponseError>(|mut writer| { + if let Err(e) = data.compute_stats(&mut writer, &index_uid) { + error!("Impossible to compute stats; {}", e) } - }; - if let Err(e) = data.compute_stats(&mut writer, &index_uid) { - error!("Impossible to compute stats; {}", e) - } + if let Err(e) = data.db.set_last_update(&mut writer, &Utc::now()) { + error!("Impossible to update last_update; {}", e) + } - if let Err(e) = data.set_last_update(&mut writer) { - error!("Impossible to update last_update; {}", e) - } - - if let Err(e) = index.main.put_updated_at(&mut writer) { - error!("Impossible to update updated_at; {}", e) - } - - if let Err(e) = writer.commit() { - error!("Impossible to get write_txn; {}", e); + if let Err(e) = index.main.put_updated_at(&mut writer) { + error!("Impossible to update updated_at; {}", e) + } + Ok(()) + }); + match res { + Ok(_) => (), + Err(e) => error!("{}", e), } } } diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index d2985f286..12e4b41f6 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -7,7 +7,7 @@ use meilisearch_core::update; use serde::Deserialize; use serde_json::Value; -use crate::error::Error; +use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; @@ -37,7 +37,7 @@ pub fn services(cfg: &mut web::ServiceConfig) { async fn get_document( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -46,13 +46,9 @@ async fn get_document( let reader = data.db.main_read_txn()?; let internal_id = index.main .external_to_internal_docid(&reader, &path.document_id)? - .ok_or(ResponseError::document_not_found(&path.document_id))?; - - let response: Document = index - .document(&reader, None, document_id)? .ok_or(Error::document_not_found(&path.document_id))?; - Ok(HttpResponse::Ok().json(response)) + Ok(HttpResponse::Ok().json(document)) } #[delete( @@ -62,7 +58,7 @@ async fn get_document( async fn delete_document( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -70,14 +66,10 @@ async fn delete_document( let document_id = meilisearch_core::serde::compute_document_id(&path.document_id); - let mut update_writer = data.db.update_write_txn()?; - let mut documents_deletion = index.documents_deletion(); documents_deletion.delete_document_by_external_docid(path.document_id.clone()); - let update_id = documents_deletion.finalize(&mut update_writer)?; - - update_writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| Ok(documents_deletion.finalize(writer)?))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -95,7 +87,7 @@ async fn get_all_documents( data: web::Data, path: web::Path, params: web::Query, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -105,7 +97,6 @@ async fn get_all_documents( let limit = params.limit.unwrap_or(20); let reader = data.db.main_read_txn()?; - let documents_ids: Result, _> = index .documents_fields_counts .documents_ids(&reader)? @@ -113,23 +104,21 @@ async fn get_all_documents( .take(limit) .collect(); - let documents_ids = documents_ids?; - let attributes: Option> = params .attributes_to_retrieve .as_ref() .map(|a| a.split(',').collect()); - let mut response = Vec::new(); - for document_id in documents_ids { + let mut documents = Vec::new(); + for document_id in documents_ids? { if let Ok(Some(document)) = index.document::(&reader, attributes.as_ref(), document_id) { - response.push(document); + documents.push(document); } } - Ok(HttpResponse::Ok().json(response)) + Ok(HttpResponse::Ok().json(documents)) } fn find_primary_key(document: &IndexMap) -> Option { @@ -153,7 +142,7 @@ async fn update_multiple_documents( params: web::Query, body: web::Json>, is_partial: bool, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -175,13 +164,14 @@ async fn update_multiple_documents( .ok_or(Error::bad_request("Could not infer a primary key"))?, }; - let mut writer = data.db.main_write_txn()?; - schema .set_primary_key(&id) .map_err(Error::bad_request)?; - index.main.put_schema(&mut writer, &schema)?; - writer.commit()?; + + data.db.main_write::<_, _, ResponseError>(|mut writer| { + index.main.put_schema(&mut writer, &schema)?; + Ok(()) + })?; } let mut document_addition = if is_partial { @@ -194,9 +184,12 @@ async fn update_multiple_documents( document_addition.update_document(document); } - let mut update_writer = data.db.update_write_txn()?; - let update_id = document_addition.finalize(&mut update_writer)?; - update_writer.commit()?; + let update_id = data + .db + .update_write::<_, _, ResponseError>(move |writer| { + let update_id = document_addition.finalize(writer)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -207,7 +200,7 @@ async fn add_documents( path: web::Path, params: web::Query, body: web::Json>, -) -> Result { +) -> Result { update_multiple_documents(data, path, params, body, false).await } @@ -217,7 +210,7 @@ async fn update_documents( path: web::Path, params: web::Query, body: web::Json>, -) -> Result { +) -> Result { update_multiple_documents(data, path, params, body, true).await } @@ -229,13 +222,12 @@ async fn delete_documents( data: web::Data, path: web::Path, body: web::Json>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let mut writer = data.db.update_write_txn()?; let mut documents_deletion = index.documents_deletion(); @@ -244,9 +236,7 @@ async fn delete_documents( documents_deletion.delete_document_by_external_docid(document_id); } - let update_id = documents_deletion.finalize(&mut writer)?; - - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| Ok(documents_deletion.finalize(writer)?))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -255,17 +245,13 @@ async fn delete_documents( async fn clear_all_documents( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let mut writer = data.db.update_write_txn()?; - - let update_id = index.clear_all(&mut writer)?; - - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| Ok(index.clear_all(writer)?))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index 316b6dd3a..eb7dfbf74 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -1,46 +1,37 @@ use actix_web::{web, HttpResponse}; use actix_web_macros::{get, put}; -use heed::types::{Str, Unit}; use serde::Deserialize; -use crate::error::Error; +use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::Data; -const UNHEALTHY_KEY: &str = "_is_unhealthy"; - pub fn services(cfg: &mut web::ServiceConfig) { cfg.service(get_health).service(change_healthyness); } #[get("/health", wrap = "Authentication::Private")] -async fn get_health(data: web::Data) -> Result { +async fn get_health(data: web::Data) -> Result { let reader = data.db.main_read_txn()?; - - let common_store = data.db.common_store(); - - if let Ok(Some(_)) = common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY) { - return Err(Error::Maintenance); + if let Ok(Some(_)) = data.db.get_health(&reader) { + return Err(Error::Maintenance.into()); } - Ok(HttpResponse::Ok().finish()) } -async fn set_healthy(data: web::Data) -> Result { - let mut writer = data.db.main_write_txn()?; - let common_store = data.db.common_store(); - common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY)?; - writer.commit()?; - +async fn set_healthy(data: web::Data) -> Result { + data.db.main_write::<_, _, ResponseError>(|writer| { + data.db.set_healthy(writer)?; + Ok(()) + })?; Ok(HttpResponse::Ok().finish()) } -async fn set_unhealthy(data: web::Data) -> Result { - let mut writer = data.db.main_write_txn()?; - let common_store = data.db.common_store(); - common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &())?; - writer.commit()?; - +async fn set_unhealthy(data: web::Data) -> Result { + data.db.main_write::<_, _, ResponseError>(|writer| { + data.db.set_unhealthy(writer)?; + Ok(()) + })?; Ok(HttpResponse::Ok().finish()) } @@ -52,8 +43,8 @@ struct HealthBody { #[put("/health", wrap = "Authentication::Private")] async fn change_healthyness( data: web::Data, - body: web::Json, -) -> Result { + body: web::Json, +) -> Result { if body.health { set_healthy(data).await } else { diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index d669f2d84..6d8d654a0 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -5,7 +5,7 @@ use log::error; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; -use crate::error::Error; +use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::IndexParam; use crate::Data; @@ -40,10 +40,9 @@ struct IndexResponse { } #[get("/indexes", wrap = "Authentication::Private")] -async fn list_indexes(data: web::Data) -> Result { +async fn list_indexes(data: web::Data) -> Result { let reader = data.db.main_read_txn()?; - - let mut response = Vec::new(); + let mut indexes = Vec::new(); for index_uid in data.db.indexes_uids() { let index = data.db.open_index(&index_uid); @@ -51,19 +50,19 @@ async fn list_indexes(data: web::Data) -> Result { match index { Some(index) => { let name = index.main.name(&reader)?.ok_or(Error::internal( - "Impossible to get the name of an index", + "Impossible to get the name of an index", ))?; let created_at = index .main .created_at(&reader)? .ok_or(Error::internal( - "Impossible to get the create date of an index", + "Impossible to get the create date of an index", ))?; let updated_at = index .main .updated_at(&reader)? .ok_or(Error::internal( - "Impossible to get the last update date of an index", + "Impossible to get the last update date of an index", ))?; let primary_key = match index.main.schema(&reader) { @@ -81,7 +80,7 @@ async fn list_indexes(data: web::Data) -> Result { updated_at, primary_key, }; - response.push(index_response); + indexes.push(index_response); } None => error!( "Index {} is referenced in the indexes list but cannot be found", @@ -90,35 +89,34 @@ async fn list_indexes(data: web::Data) -> Result { } } - Ok(HttpResponse::Ok().json(response)) + Ok(HttpResponse::Ok().json(indexes)) } #[get("/indexes/{index_uid}", wrap = "Authentication::Private")] async fn get_index( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; - let name = index.main.name(&reader)?.ok_or(Error::internal( - "Impossible to get the name of an index", + "Impossible to get the name of an index", ))?; let created_at = index .main .created_at(&reader)? .ok_or(Error::internal( - "Impossible to get the create date of an index", + "Impossible to get the create date of an index", ))?; let updated_at = index .main .updated_at(&reader)? .ok_or(Error::internal( - "Impossible to get the last update date of an index", + "Impossible to get the last update date of an index", ))?; let primary_key = match index.main.schema(&reader) { @@ -128,14 +126,15 @@ async fn get_index( }, _ => None, }; - - Ok(HttpResponse::Ok().json(IndexResponse { + let index_response = IndexResponse { name, uid: path.index_uid.clone(), created_at, updated_at, primary_key, - })) + }; + + Ok(HttpResponse::Ok().json(index_response)) } #[derive(Debug, Deserialize)] @@ -150,11 +149,11 @@ struct IndexCreateRequest { async fn create_index( data: web::Data, body: web::Json, -) -> Result { +) -> Result { if let (None, None) = (body.name.clone(), body.uid.clone()) { return Err(Error::bad_request( "Index creation must have an uid", - )); + ).into()); } let uid = match &body.uid { @@ -165,7 +164,7 @@ async fn create_index( { uid.to_owned() } else { - return Err(Error::InvalidIndexUid); + return Err(Error::InvalidIndexUid.into()); } } None => loop { @@ -181,39 +180,39 @@ async fn create_index( .create_index(&uid) .map_err(Error::create_index)?; - let mut writer = data.db.main_write_txn()?; + let index_response = data.db.main_write::<_, _, ResponseError>(|mut writer| { + let name = body.name.as_ref().unwrap_or(&uid); + created_index.main.put_name(&mut writer, name)?; - let name = body.name.as_ref().unwrap_or(&uid); - created_index.main.put_name(&mut writer, name)?; + let created_at = created_index + .main + .created_at(&writer)? + .ok_or(Error::internal("Impossible to read created at"))?; - let created_at = created_index - .main - .created_at(&writer)? - .ok_or(Error::internal("Impossible to read created at"))?; + let updated_at = created_index + .main + .updated_at(&writer)? + .ok_or(Error::internal("Impossible to read updated at"))?; - let updated_at = created_index - .main - .updated_at(&writer)? - .ok_or(Error::internal("Impossible to read updated at"))?; - - if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = created_index.main.schema(&writer)? { - schema - .set_primary_key(&id) - .map_err(Error::bad_request)?; - created_index.main.put_schema(&mut writer, &schema)?; + if let Some(id) = body.primary_key.clone() { + if let Some(mut schema) = created_index.main.schema(&writer)? { + schema + .set_primary_key(&id) + .map_err(Error::bad_request)?; + created_index.main.put_schema(&mut writer, &schema)?; + } } - } + let index_response = IndexResponse { + name: name.to_string(), + uid, + created_at, + updated_at, + primary_key: body.primary_key.clone(), + }; + Ok(index_response) + })?; - writer.commit()?; - - Ok(HttpResponse::Created().json(IndexResponse { - name: name.to_string(), - uid, - created_at, - updated_at, - primary_key: body.primary_key.clone(), - })) + Ok(HttpResponse::Created().json(index_response)) } #[derive(Debug, Deserialize)] @@ -238,53 +237,51 @@ async fn update_index( data: web::Data, path: web::Path, body: web::Json, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let mut writer = data.db.main_write_txn()?; + data.db.main_write::<_, _, ResponseError>(|mut writer| { + if let Some(name) = &body.name { + index.main.put_name(&mut writer, name)?; + } - if let Some(name) = &body.name { - index.main.put_name(&mut writer, name)?; - } - - if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = index.main.schema(&writer)? { - match schema.primary_key() { - Some(_) => { - return Err(Error::bad_request( - "The primary key cannot be updated", - )); - } - None => { - schema.set_primary_key(&id)?; - index.main.put_schema(&mut writer, &schema)?; + if let Some(id) = body.primary_key.clone() { + if let Some(mut schema) = index.main.schema(&writer)? { + match schema.primary_key() { + Some(_) => { + return Err(Error::bad_request( + "The primary key cannot be updated", + ).into()); + } + None => { + schema.set_primary_key(&id)?; + index.main.put_schema(&mut writer, &schema)?; + } } } } - } - - index.main.put_updated_at(&mut writer)?; - writer.commit()?; + index.main.put_updated_at(&mut writer)?; + Ok(()) + })?; let reader = data.db.main_read_txn()?; - let name = index.main.name(&reader)?.ok_or(Error::internal( - "Impossible to get the name of an index", + "Impossible to get the name of an index", ))?; let created_at = index .main .created_at(&reader)? .ok_or(Error::internal( - "Impossible to get the create date of an index", + "Impossible to get the create date of an index", ))?; let updated_at = index .main .updated_at(&reader)? .ok_or(Error::internal( - "Impossible to get the last update date of an index", + "Impossible to get the last update date of an index", ))?; let primary_key = match index.main.schema(&reader) { @@ -295,20 +292,22 @@ async fn update_index( _ => None, }; - Ok(HttpResponse::Ok().json(IndexResponse { + let index_response = IndexResponse { name, uid: path.index_uid.clone(), created_at, updated_at, primary_key, - })) + }; + + Ok(HttpResponse::Ok().json(index_response)) } #[delete("/indexes/{index_uid}", wrap = "Authentication::Private")] async fn delete_index( data: web::Data, path: web::Path, -) -> Result { +) -> Result { data.db.delete_index(&path.index_uid)?; Ok(HttpResponse::NoContent().finish()) @@ -327,7 +326,7 @@ struct UpdateParam { async fn get_update_status( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -342,7 +341,7 @@ async fn get_update_status( None => Err(Error::NotFound(format!( "Update {} not found", path.update_id - ))), + )).into()), } } @@ -350,7 +349,7 @@ async fn get_update_status( async fn get_all_updates_status( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 4224edf79..b0ee0e6bf 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -7,7 +7,7 @@ use actix_web_macros::get; use serde::Deserialize; use serde_json::Value; -use crate::error::{Error, FacetCountError}; +use crate::error::{Error, FacetCountError, ResponseError}; use crate::helpers::meilisearch::IndexSearchExt; use crate::helpers::Authentication; use crate::routes::IndexParam; @@ -41,14 +41,13 @@ async fn search_with_url_query( data: web::Data, path: web::Path, params: web::Query, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; - let schema = index .main .schema(&reader)? @@ -88,9 +87,9 @@ async fn search_with_url_query( } if let Some(ref facet_filters) = params.facet_filters { - match index.main.attributes_for_faceting(&reader)? { - Some(ref attrs) => { search_builder.add_facet_filters(FacetFilter::from_str(facet_filters, &schema, attrs)?); }, - None => return Err(Error::FacetExpression("can't filter on facets, as no facet is set".to_string())) + let attrs = index.main.attributes_for_faceting(&reader)?; + if let Some(attrs) = attrs { + search_builder.add_facet_filters(FacetFilter::from_str(facet_filters, &schema, &attrs)?); } } @@ -100,7 +99,7 @@ async fn search_with_url_query( let field_ids = prepare_facet_list(&facets, &schema, attrs)?; search_builder.add_facets(field_ids); }, - None => return Err(FacetCountError::NoFacetSet.into()) + None => todo!() /* return Err(FacetCountError::NoFacetSet.into()) */ } } @@ -160,8 +159,9 @@ async fn search_with_url_query( search_builder.get_matches(); } } + let search_result = search_builder.search(&reader)?; - Ok(HttpResponse::Ok().json(search_builder.search(&reader)?)) + Ok(HttpResponse::Ok().json(search_result)) } /// Parses the incoming string into an array of attributes for which to return a count. It returns diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 8d5c48530..99ea052a9 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -3,7 +3,7 @@ use actix_web_macros::{delete, get, post}; use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES}; use std::collections::{BTreeMap, BTreeSet, HashSet}; -use crate::error::Error; +use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; @@ -33,19 +33,20 @@ async fn update_all( data: web::Data, path: web::Path, body: web::Json, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let mut writer = data.db.update_write_txn()?; - let settings = body - .into_inner() - .into_update() - .map_err(Error::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let settings = body + .into_inner() + .into_update() + .map_err(Error::bad_request)?; + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -54,7 +55,7 @@ async fn update_all( async fn get_all( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -62,19 +63,21 @@ async fn get_all( let reader = data.db.main_read_txn()?; - let stop_words_fst = index.main.stop_words_fst(&reader)?; - let stop_words = stop_words_fst.stream().into_strs()?; - let stop_words: BTreeSet = stop_words.into_iter().collect(); + let stop_words: BTreeSet = index + .main + .stop_words_list(&reader)? + .into_iter() + .collect(); - let synonyms_fst = index.main.synonyms_fst(&reader)?; - let synonyms_list = synonyms_fst.stream().into_strs()?; + let synonyms_list = index.main.synonyms_list(&reader)?; let mut synonyms = BTreeMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; - let list = alternative_list.stream().into_strs()?; - synonyms.insert(synonym, list); + if let Some(list) = alternative_list { + synonyms.insert(synonym, list); + } } let ranking_rules = index @@ -134,12 +137,11 @@ async fn get_all( async fn delete_all( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { ranking_rules: UpdateState::Clear, @@ -153,8 +155,10 @@ async fn delete_all( attributes_for_faceting: UpdateState::Clear, }; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -166,7 +170,7 @@ async fn delete_all( async fn get_rules( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -192,7 +196,7 @@ async fn update_rules( data: web::Data, path: web::Path, body: web::Json>>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -203,10 +207,12 @@ async fn update_rules( ..Settings::default() }; - let mut writer = data.db.update_write_txn()?; let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -218,21 +224,21 @@ async fn update_rules( async fn delete_rules( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { ranking_rules: UpdateState::Clear, ..SettingsUpdate::default() }; - let update_id = index.settings_update(&mut writer, settings)?; - - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -244,7 +250,7 @@ async fn delete_rules( async fn get_distinct( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -263,7 +269,7 @@ async fn update_distinct( data: web::Data, path: web::Path, body: web::Json>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -274,10 +280,12 @@ async fn update_distinct( ..Settings::default() }; - let mut writer = data.db.update_write_txn()?; let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -289,21 +297,21 @@ async fn update_distinct( async fn delete_distinct( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { distinct_attribute: UpdateState::Clear, ..SettingsUpdate::default() }; - let update_id = index.settings_update(&mut writer, settings)?; - - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -315,7 +323,7 @@ async fn delete_distinct( async fn get_searchable( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -336,7 +344,7 @@ async fn update_searchable( data: web::Data, path: web::Path, body: web::Json>>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -347,10 +355,12 @@ async fn update_searchable( ..Settings::default() }; - let mut writer = data.db.update_write_txn()?; let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -362,7 +372,7 @@ async fn update_searchable( async fn delete_searchable( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -373,9 +383,10 @@ async fn delete_searchable( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn()?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -387,7 +398,7 @@ async fn delete_searchable( async fn get_displayed( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -410,7 +421,7 @@ async fn update_displayed( data: web::Data, path: web::Path, body: web::Json>>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -421,10 +432,12 @@ async fn update_displayed( ..Settings::default() }; - let mut writer = data.db.update_write_txn()?; let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -436,7 +449,7 @@ async fn update_displayed( async fn delete_displayed( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -447,9 +460,10 @@ async fn delete_displayed( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn()?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -461,7 +475,7 @@ async fn delete_displayed( async fn get_accept_new_fields( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -483,7 +497,7 @@ async fn update_accept_new_fields( data: web::Data, path: web::Path, body: web::Json>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -494,10 +508,12 @@ async fn update_accept_new_fields( ..Settings::default() }; - let mut writer = data.db.update_write_txn()?; let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index e7de46b42..5d5988518 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -10,7 +10,7 @@ use serde::Serialize; use sysinfo::{NetworkExt, ProcessExt, ProcessorExt, System, SystemExt}; use walkdir::WalkDir; -use crate::error::Error; +use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::IndexParam; use crate::Data; @@ -35,7 +35,7 @@ struct IndexStatsResponse { async fn index_stats( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -71,7 +71,7 @@ struct StatsResult { } #[get("/stats", wrap = "Authentication::Private")] -async fn get_stats(data: web::Data) -> Result { +async fn get_stats(data: web::Data) -> Result { let mut index_list = HashMap::new(); let reader = data.db.main_read_txn()?; @@ -111,7 +111,7 @@ async fn get_stats(data: web::Data) -> Result { .filter(|metadata| metadata.is_file()) .fold(0, |acc, m| acc + m.len()); - let last_update = data.last_update(&reader)?; + let last_update = data.db.last_update(&reader)?; Ok(HttpResponse::Ok().json(StatsResult { database_size, diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index f09dd2442..b420c149b 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -3,7 +3,7 @@ use actix_web_macros::{delete, get, post}; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; use std::collections::BTreeSet; -use crate::error::Error; +use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; @@ -19,14 +19,13 @@ pub fn services(cfg: &mut web::ServiceConfig) { async fn get( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; - let stop_words_fst = index.main.stop_words_fst(&reader)?; - let stop_words = stop_words_fst.stream().into_strs()?; + let stop_words = index.main.stop_words_list(&reader)?; Ok(HttpResponse::Ok().json(stop_words)) } @@ -39,7 +38,7 @@ async fn update( data: web::Data, path: web::Path, body: web::Json>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -50,9 +49,10 @@ async fn update( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn()?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -64,7 +64,7 @@ async fn update( async fn delete( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -75,9 +75,10 @@ async fn delete( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn()?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index fd3888e29..c334297af 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -5,7 +5,7 @@ use actix_web_macros::{delete, get, post}; use indexmap::IndexMap; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use crate::error::Error; +use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; @@ -21,7 +21,7 @@ pub fn services(cfg: &mut web::ServiceConfig) { async fn get( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -29,15 +29,15 @@ async fn get( let reader = data.db.main_read_txn()?; - let synonyms_fst = index.main.synonyms_fst(&reader)?; - let synonyms_list = synonyms_fst.stream().into_strs()?; + let synonyms_list = index.main.synonyms_list(&reader)?; let mut synonyms = IndexMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; - let list = alternative_list.stream().into_strs()?; - synonyms.insert(synonym, list); + if let Some(list) = alternative_list { + synonyms.insert(synonym, list); + } } Ok(HttpResponse::Ok().json(synonyms)) @@ -51,7 +51,7 @@ async fn update( data: web::Data, path: web::Path, body: web::Json>>, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -62,9 +62,10 @@ async fn update( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn()?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -76,7 +77,7 @@ async fn update( async fn delete( data: web::Data, path: web::Path, -) -> Result { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -87,10 +88,10 @@ async fn delete( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn()?; - let update_id = index.settings_update(&mut writer, settings)?; - - writer.commit()?; + let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = index.settings_update(&mut writer, settings)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/tests/documents_delete.rs b/meilisearch-http/tests/documents_delete.rs index 7edc9ac63..48b8f70ee 100644 --- a/meilisearch-http/tests/documents_delete.rs +++ b/meilisearch-http/tests/documents_delete.rs @@ -21,6 +21,7 @@ async fn delete_batch() { server.populate_movies().await; let (_response, status_code) = server.get_document(419704).await; + println!("{:?}", _response); assert_eq!(status_code, 200); let body = serde_json::json!([419704, 512200, 181812]); diff --git a/meilisearch-schema/Cargo.toml b/meilisearch-schema/Cargo.toml index 712fdb008..a795fa1db 100644 --- a/meilisearch-schema/Cargo.toml +++ b/meilisearch-schema/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] bincode = "1.2.1" indexmap = { version = "1.3.2", features = ["serde-1"] } +meilisearch-error = { path = "../meilisearch-error", version = "0.10.1" } serde = { version = "1.0.105", features = ["derive"] } serde_json = { version = "1.0.50", features = ["preserve_order"] } toml = { version = "0.5.6", features = ["preserve_order"] } diff --git a/meilisearch-schema/src/error.rs b/meilisearch-schema/src/error.rs index c31596df2..c1b0ffb3f 100644 --- a/meilisearch-schema/src/error.rs +++ b/meilisearch-schema/src/error.rs @@ -1,6 +1,7 @@ - use std::{error, fmt}; +use meilisearch_error::{ErrorCode, Code}; + pub type SResult = Result; #[derive(Debug)] @@ -22,3 +23,9 @@ impl fmt::Display for Error { } impl error::Error for Error {} + +impl ErrorCode for Error { + fn error_code(&self) -> Code { + unimplemented!() + } +} From c771694623fd8c4b933b9cfba09e9b7cbb6144ff Mon Sep 17 00:00:00 2001 From: mpostma Date: Fri, 22 May 2020 12:35:23 +0200 Subject: [PATCH 04/18] remove heed from http dependencies --- Cargo.lock | 1 - meilisearch-core/src/database.rs | 49 +++++++++++++++++++- meilisearch-core/src/lib.rs | 6 +++ meilisearch-http/Cargo.toml | 1 - meilisearch-http/src/data.rs | 51 +-------------------- meilisearch-http/src/helpers/meilisearch.rs | 8 ++-- meilisearch-http/src/lib.rs | 2 +- meilisearch-http/src/routes/stats.rs | 4 +- 8 files changed, 61 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc7454ea6..ea62dfe64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1698,7 +1698,6 @@ dependencies = [ "crossbeam-channel", "env_logger", "futures", - "heed", "http 0.1.21", "http-service", "http-service-mock", diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index 09fc2b326..7e44c7f7d 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -8,10 +8,10 @@ use chrono::{DateTime, Utc}; use crossbeam_channel::{Receiver, Sender}; use heed::types::{Str, Unit, SerdeBincode}; use heed::{CompactionOption, Result as ZResult}; -use log::debug; +use log::{debug, error}; use meilisearch_schema::Schema; -use crate::{store, update, Index, MResult, Error}; +use crate::{store, update, Index, MResult, Error, UpdateReader, MainWriter}; pub type BoxUpdateFn = Box; type ArcSwapFn = arc_swap::ArcSwapOption; @@ -247,6 +247,13 @@ impl Database { } } + pub fn is_indexing(&self, reader: &UpdateReader, index: &str) -> MResult> { + match self.open_index(&index) { + Some(index) => index.current_update_id(&reader).map(|u| Some(u.is_some())), + None => Ok(None), + } + } + pub fn create_index(&self, name: impl AsRef) -> MResult { let name = name.as_ref(); let mut indexes_lock = self.indexes.write().unwrap(); @@ -449,6 +456,44 @@ impl Database { let common_store = self.common_store(); Ok(common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY)?) } + + pub fn compute_stats(&self, writer: &mut MainWriter, index_uid: &str) -> MResult<()> { + let index = match self.open_index(&index_uid) { + Some(index) => index, + None => { + error!("Impossible to retrieve index {}", index_uid); + return Ok(()); + } + }; + + let schema = match index.main.schema(&writer)? { + Some(schema) => schema, + None => return Ok(()), + }; + + let all_documents_fields = index + .documents_fields_counts + .all_documents_fields_counts(&writer)?; + + // count fields frequencies + let mut fields_frequency = HashMap::<_, usize>::new(); + for result in all_documents_fields { + let (_, attr, _) = result?; + if let Some(field_id) = schema.indexed_pos_to_field_id(attr) { + *fields_frequency.entry(field_id).or_default() += 1; + } + } + + // convert attributes to their names + let frequency: HashMap<_, _> = fields_frequency + .into_iter() + .filter_map(|(a, c)| schema.name(a).map(|name| (name.to_string(), c))) + .collect(); + + index + .main + .put_fields_frequency(writer, &frequency) + } } #[cfg(test)] diff --git a/meilisearch-core/src/lib.rs b/meilisearch-core/src/lib.rs index a9938bb73..d837bae62 100644 --- a/meilisearch-core/src/lib.rs +++ b/meilisearch-core/src/lib.rs @@ -52,6 +52,12 @@ use crate::reordered_attrs::ReorderedAttrs; type FstSetCow<'a> = fst::Set>; type FstMapCow<'a> = fst::Map>; +pub type MainWriter<'a> = heed::RwTxn<'a, MainT>; +pub type MainReader = heed::RoTxn; + +pub type UpdateWriter<'a> = heed::RwTxn<'a, UpdateT>; +pub type UpdateReader = heed::RoTxn; + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Document { pub id: DocumentId, diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index 20f4d4f4e..abcbba9d2 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -29,7 +29,6 @@ chrono = { version = "0.4.11", features = ["serde"] } crossbeam-channel = "0.4.2" env_logger = "0.7.1" futures = "0.3.4" -heed = "0.8.0" http = "0.1.19" indexmap = { version = "1.3.2", features = ["serde-1"] } log = "0.4.8" diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index e278f0ffc..15c2ff1af 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -1,9 +1,7 @@ -use std::collections::HashMap; use std::ops::Deref; use std::sync::Arc; -use log::error; -use meilisearch_core::{Database, DatabaseOptions, MResult, MainT, UpdateT}; +use meilisearch_core::{Database, DatabaseOptions}; use sha2::Digest; use sysinfo::Pid; @@ -56,53 +54,6 @@ impl ApiKeys { } } -impl DataInner { - pub fn is_indexing(&self, reader: &heed::RoTxn, index: &str) -> MResult> { - match self.db.open_index(&index) { - Some(index) => index.current_update_id(&reader).map(|u| Some(u.is_some())), - None => Ok(None), - } - } - - pub fn compute_stats(&self, writer: &mut heed::RwTxn, index_uid: &str) -> MResult<()> { - let index = match self.db.open_index(&index_uid) { - Some(index) => index, - None => { - error!("Impossible to retrieve index {}", index_uid); - return Ok(()); - } - }; - - let schema = match index.main.schema(&writer)? { - Some(schema) => schema, - None => return Ok(()), - }; - - let all_documents_fields = index - .documents_fields_counts - .all_documents_fields_counts(&writer)?; - - // count fields frequencies - let mut fields_distribution = HashMap::<_, usize>::new(); - for result in all_documents_fields { - let (_, attr, _) = result?; - if let Some(field_id) = schema.indexed_pos_to_field_id(attr) { - *fields_distribution.entry(field_id).or_default() += 1; - } - } - - // convert attributes to their names - let distribution: HashMap<_, _> = fields_distribution - .into_iter() - .filter_map(|(a, c)| schema.name(a).map(|name| (name.to_string(), c))) - .collect(); - - index - .main - .put_fields_distribution(writer, &distribution) - } -} - impl Data { pub fn new(opt: Opt) -> Data { let db_path = opt.db_path.clone(); diff --git a/meilisearch-http/src/helpers/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index f760bb1ac..65b6da08f 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -5,11 +5,11 @@ use std::time::Instant; use indexmap::IndexMap; use log::error; -use meilisearch_core::Filter; +use meilisearch_core::{Filter, MainReader}; use meilisearch_core::facets::FacetFilter; use meilisearch_core::criterion::*; use meilisearch_core::settings::RankingRule; -use meilisearch_core::{Highlight, Index, MainT, RankedMap}; +use meilisearch_core::{Highlight, Index, RankedMap}; use meilisearch_schema::{FieldId, Schema}; use meilisearch_tokenizer::is_cjk; use serde::{Deserialize, Serialize}; @@ -107,7 +107,7 @@ impl<'a> SearchBuilder<'a> { self } - pub fn search(self, reader: &heed::RoTxn) -> Result { + pub fn search(self, reader: &MainReader) -> Result { let schema = self .index .main @@ -257,7 +257,7 @@ impl<'a> SearchBuilder<'a> { pub fn get_criteria( &self, - reader: &heed::RoTxn, + reader: &MainReader, ranked_map: &'a RankedMap, schema: &Schema, ) -> Result>, ResponseError> { diff --git a/meilisearch-http/src/lib.rs b/meilisearch-http/src/lib.rs index 5a44d5761..8e81d9221 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -59,7 +59,7 @@ pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpda if let Some(index) = data.db.open_index(&index_uid) { let db = &data.db; let res = db.main_write::<_, _, ResponseError>(|mut writer| { - if let Err(e) = data.compute_stats(&mut writer, &index_uid) { + if let Err(e) = data.db.compute_stats(&mut writer, &index_uid) { error!("Impossible to compute stats; {}", e) } diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index 5d5988518..5f3e09dd5 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -50,7 +50,7 @@ async fn index_stats( let update_reader = data.db.update_read_txn()?; let is_indexing = - data.is_indexing(&update_reader, &path.index_uid)? + data.db.is_indexing(&update_reader, &path.index_uid)? .ok_or(Error::internal( "Impossible to know if the database is indexing", ))?; @@ -86,7 +86,7 @@ async fn get_stats(data: web::Data) -> Result let fields_distribution = index.main.fields_distribution(&reader)?.unwrap_or_default(); - let is_indexing = data.is_indexing(&update_reader, &index_uid)?.ok_or( + let is_indexing = data.db.is_indexing(&update_reader, &index_uid)?.ok_or( Error::internal("Impossible to know if the database is indexing"), )?; From 3bd15a4195b68d9191110b07c27084fcdcce46d1 Mon Sep 17 00:00:00 2001 From: mpostma Date: Fri, 22 May 2020 18:04:23 +0200 Subject: [PATCH 05/18] fix tests, restore behavior --- meilisearch-core/examples/from_file.rs | 17 ++- meilisearch-core/src/database.rs | 2 +- meilisearch-core/src/error.rs | 2 +- meilisearch-core/src/store/main.rs | 58 +++------ meilisearch-core/src/store/synonyms.rs | 6 +- meilisearch-error/src/lib.rs | 144 +++++++++++++++------ meilisearch-http/src/error.rs | 27 +++- meilisearch-http/src/routes/document.rs | 12 +- meilisearch-http/src/routes/health.rs | 2 +- meilisearch-http/src/routes/search.rs | 2 +- meilisearch-http/src/routes/setting.rs | 46 ++++--- meilisearch-http/src/routes/stop_words.rs | 8 +- meilisearch-http/src/routes/synonym.rs | 14 +- meilisearch-http/tests/documents_delete.rs | 2 +- meilisearch-http/tests/index.rs | 18 ++- meilisearch-schema/src/error.rs | 3 +- 16 files changed, 218 insertions(+), 145 deletions(-) diff --git a/meilisearch-core/examples/from_file.rs b/meilisearch-core/examples/from_file.rs index 821f23fcc..561036c02 100644 --- a/meilisearch-core/examples/from_file.rs +++ b/meilisearch-core/examples/from_file.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use structopt::StructOpt; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; -use meilisearch_core::{Database, DatabaseOptions, Highlight, ProcessedUpdateResult}; +use meilisearch_core::{Database, DatabaseOptions, Highlight, ProcessedUpdateResult, Error as MError}; use meilisearch_core::settings::Settings; use meilisearch_schema::FieldId; @@ -126,9 +126,10 @@ fn index_command(command: IndexCommand, database: Database) -> Result<(), Box(|writer| { + index.settings_update(writer, settings)?; + Ok(()) + })?; let mut rdr = if command.csv_data_path.as_os_str() == "-" { csv::Reader::from_reader(Box::new(io::stdin()) as Box) @@ -175,10 +176,12 @@ fn index_command(command: IndexCommand, database: Database) -> Result<(), Box(|writer| { + let update_id = additions.finalize(writer)?; + Ok(update_id) + })?; + println!("committing update..."); - let update_id = additions.finalize(&mut update_writer)?; - update_writer.commit().unwrap(); max_update_id = max_update_id.max(update_id); println!("committed update {}", update_id); } diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index 7e44c7f7d..27b66cdf3 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -390,7 +390,7 @@ impl Database { f(&reader) } - pub fn update_write_txn(&self) -> MResult> { + pub(crate) fn update_write_txn(&self) -> MResult> { Ok(self.update_env.typed_write_txn::()?) } diff --git a/meilisearch-core/src/error.rs b/meilisearch-core/src/error.rs index e01816423..962468268 100644 --- a/meilisearch-core/src/error.rs +++ b/meilisearch-core/src/error.rs @@ -37,7 +37,7 @@ pub enum Error { impl ErrorCode for Error { fn error_code(&self) -> Code { //TODO populate codes - Code::Other + Code::Internal } } diff --git a/meilisearch-core/src/store/main.rs b/meilisearch-core/src/store/main.rs index 7873b3dba..21b506307 100644 --- a/meilisearch-core/src/store/main.rs +++ b/meilisearch-core/src/store/main.rs @@ -71,43 +71,42 @@ impl Main { Ok(self.main.get::<_, Str, SerdeDatetime>(reader, UPDATED_AT_KEY)?) } -<<<<<<< HEAD - pub fn put_internal_docids(self, writer: &mut heed::RwTxn, ids: &sdset::Set) -> ZResult<()> { - self.main.put::<_, Str, DocumentsIds>(writer, INTERNAL_DOCIDS_KEY, ids) + pub fn put_internal_docids(self, writer: &mut heed::RwTxn, ids: &sdset::Set) -> MResult<()> { + Ok(self.main.put::<_, Str, DocumentsIds>(writer, INTERNAL_DOCIDS_KEY, ids)?) } - pub fn internal_docids<'txn>(self, reader: &'txn heed::RoTxn) -> ZResult>> { + pub fn internal_docids<'txn>(self, reader: &'txn heed::RoTxn) -> MResult>> { match self.main.get::<_, Str, DocumentsIds>(reader, INTERNAL_DOCIDS_KEY)? { Some(ids) => Ok(ids), None => Ok(Cow::default()), } } - pub fn merge_internal_docids(self, writer: &mut heed::RwTxn, new_ids: &sdset::Set) -> ZResult<()> { + pub fn merge_internal_docids(self, writer: &mut heed::RwTxn, new_ids: &sdset::Set) -> MResult<()> { use sdset::SetOperation; // We do an union of the old and new internal ids. let internal_docids = self.internal_docids(writer)?; let internal_docids = sdset::duo::Union::new(&internal_docids, new_ids).into_set_buf(); - self.put_internal_docids(writer, &internal_docids) + Ok(self.put_internal_docids(writer, &internal_docids)?) } - pub fn remove_internal_docids(self, writer: &mut heed::RwTxn, ids: &sdset::Set) -> ZResult<()> { + pub fn remove_internal_docids(self, writer: &mut heed::RwTxn, ids: &sdset::Set) -> MResult<()> { use sdset::SetOperation; // We do a difference of the old and new internal ids. let internal_docids = self.internal_docids(writer)?; let internal_docids = sdset::duo::Difference::new(&internal_docids, ids).into_set_buf(); - self.put_internal_docids(writer, &internal_docids) + Ok(self.put_internal_docids(writer, &internal_docids)?) } - pub fn put_external_docids(self, writer: &mut heed::RwTxn, ids: &fst::Map) -> ZResult<()> + pub fn put_external_docids(self, writer: &mut heed::RwTxn, ids: &fst::Map) -> MResult<()> where A: AsRef<[u8]>, { - self.main.put::<_, Str, ByteSlice>(writer, EXTERNAL_DOCIDS_KEY, ids.as_fst().as_bytes()) + Ok(self.main.put::<_, Str, ByteSlice>(writer, EXTERNAL_DOCIDS_KEY, ids.as_fst().as_bytes())?) } - pub fn merge_external_docids(self, writer: &mut heed::RwTxn, new_docids: &fst::Map) -> ZResult<()> + pub fn merge_external_docids(self, writer: &mut heed::RwTxn, new_docids: &fst::Map) -> MResult<()> where A: AsRef<[u8]>, { use fst::{Streamer, IntoStreamer}; @@ -118,29 +117,14 @@ impl Main { let mut build = fst::MapBuilder::memory(); while let Some((docid, values)) = op.next() { build.insert(docid, values[0].value).unwrap(); -======= - pub fn put_words_fst(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> MResult<()> { - let bytes = fst.as_fst().as_bytes(); - Ok(self.main.put::<_, Str, ByteSlice>(writer, WORDS_KEY, bytes)?) - } - - pub unsafe fn static_words_fst(self, reader: &heed::RoTxn) -> MResult> { - match self.main.get::<_, Str, ByteSlice>(reader, WORDS_KEY)? { - Some(bytes) => { - let bytes: &'static [u8] = std::mem::transmute(bytes); - let set = fst::Set::from_static_slice(bytes).unwrap(); - Ok(Some(set)) - } - None => Ok(None), ->>>>>>> 5c760d3... refactor errors / isolate core/http errors } drop(op); let external_docids = build.into_map(); - self.put_external_docids(writer, &external_docids) + Ok(self.put_external_docids(writer, &external_docids)?) } - pub fn remove_external_docids(self, writer: &mut heed::RwTxn, ids: &fst::Map) -> ZResult<()> + pub fn remove_external_docids(self, writer: &mut heed::RwTxn, ids: &fst::Map) -> MResult<()> where A: AsRef<[u8]>, { use fst::{Streamer, IntoStreamer}; @@ -158,29 +142,29 @@ impl Main { self.put_external_docids(writer, &external_docids) } - pub fn external_docids(self, reader: &heed::RoTxn) -> ZResult { + pub fn external_docids(self, reader: &heed::RoTxn) -> MResult { match self.main.get::<_, Str, ByteSlice>(reader, EXTERNAL_DOCIDS_KEY)? { Some(bytes) => Ok(fst::Map::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()), None => Ok(fst::Map::default().map_data(Cow::Owned).unwrap()), } } - pub fn external_to_internal_docid(self, reader: &heed::RoTxn, external_docid: &str) -> ZResult> { + pub fn external_to_internal_docid(self, reader: &heed::RoTxn, external_docid: &str) -> MResult> { let external_ids = self.external_docids(reader)?; Ok(external_ids.get(external_docid).map(|id| DocumentId(id as u32))) } - pub fn put_words_fst>(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> ZResult<()> { - self.main.put::<_, Str, ByteSlice>(writer, WORDS_KEY, fst.as_fst().as_bytes()) - } - - pub fn words_fst(self, reader: &heed::RoTxn) -> MResult> { + pub fn words_fst(self, reader: &heed::RoTxn) -> MResult { match self.main.get::<_, Str, ByteSlice>(reader, WORDS_KEY)? { Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()), None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()), } } + pub fn put_words_fst>(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> MResult<()> { + Ok(self.main.put::<_, Str, ByteSlice>(writer, WORDS_KEY, fst.as_fst().as_bytes())?) + } + pub fn put_schema(self, writer: &mut heed::RwTxn, schema: &Schema) -> MResult<()> { Ok(self.main.put::<_, Str, SerdeBincode>(writer, SCHEMA_KEY, schema)?) } @@ -206,7 +190,7 @@ impl Main { Ok(self.main.put::<_, Str, ByteSlice>(writer, SYNONYMS_KEY, bytes)?) } - pub(crate) fn synonyms_fst(self, reader: &heed::RoTxn) -> MResult> { + pub(crate) fn synonyms_fst(self, reader: &heed::RoTxn) -> MResult { match self.main.get::<_, Str, ByteSlice>(reader, SYNONYMS_KEY)? { Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()), None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()), @@ -216,7 +200,6 @@ impl Main { pub fn synonyms_list(self, reader: &heed::RoTxn) -> MResult> { let synonyms = self .synonyms_fst(&reader)? - .unwrap_or_default() .stream() .into_strs()?; Ok(synonyms) @@ -237,7 +220,6 @@ impl Main { pub fn stop_words_list(self, reader: &heed::RoTxn) -> MResult> { let stop_word_list = self .stop_words_fst(reader)? - .unwrap_or_default() .stream() .into_strs()?; Ok(stop_word_list) diff --git a/meilisearch-core/src/store/synonyms.rs b/meilisearch-core/src/store/synonyms.rs index cf7cf5811..699667fcd 100644 --- a/meilisearch-core/src/store/synonyms.rs +++ b/meilisearch-core/src/store/synonyms.rs @@ -34,11 +34,11 @@ impl Synonyms { } } - pub fn synonyms(self, reader: &heed::RoTxn, word: &[u8]) -> MResult>> { + pub fn synonyms(self, reader: &heed::RoTxn, word: &[u8]) -> MResult> { let synonyms = self .synonyms_fst(&reader, word)? - .map(|list| list.stream().into_strs()) - .transpose()?; + .stream() + .into_strs()?; Ok(synonyms) } } diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index d82b81b1b..59715ef9a 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -12,9 +12,80 @@ enum ErrorCategory { } pub enum Code { + BadParameter, + BadRequest, + CreateIndex, + DocumentNotFound, + IndexNotFound, + Internal, + InvalidIndexUid, + InvalidToken, + Maintenance, + MissingAuthorizationHeader, + MissingHeader, + NotFound, + OpenIndex, + RetrieveDocument, + SearchDocuments, + PayloadTooLarge, + UnsupportedMediaType, Other, } +impl Code { + + /// ascociate a `Code` variant to the actual ErrCode + fn err_code(&self) -> ErrCode { + use Code::*; + + match self { + BadParameter + | BadRequest + | CreateIndex + | InvalidIndexUid + | OpenIndex + | RetrieveDocument + | SearchDocuments => ErrCode::bad_request(false, false, ErrorCategory::None, 0), + DocumentNotFound + | IndexNotFound + | NotFound => ErrCode::not_found(false, false, ErrorCategory::None, 0), + InvalidToken + | MissingHeader => ErrCode::unauthorized(false, false, ErrorCategory::None, 0), + MissingAuthorizationHeader => ErrCode::forbidden(false, false, ErrorCategory::None, 0), + Internal => ErrCode::internal(false, false, ErrorCategory::None, 0), + Maintenance => ErrCode::service_unavailable(false, false, ErrorCategory::None, 0), + PayloadTooLarge => ErrCode::payload_too_large(false, false, ErrorCategory::None, 0), + UnsupportedMediaType => ErrCode::unsupported_media_type(false, false, ErrorCategory::None, 0), + _ => ErrCode::not_found(false, false, ErrorCategory::None, 0), + } + } + + /// return the HTTP status code ascociated with the `Code` + pub fn http(&self) -> StatusCode { + self.err_code().http_code + } + + /// returns internal error code, in the form: + /// `EPFCNN` + /// - E: plain letter "E", to mark an error code, future main introduce W for warning + /// - P: scope of the error, 0 for private, 1 for public. Private are error that make no sense + /// reporting to the user, they are internal errors, and there is nothing the user can do about + /// them. they are nonetheless returned, without a message, for assistance purpose. + /// - F: 0 or 1, report if the error is fatal. + /// - C: error category, number in 0-9, the category of the error. Categories are still to be determined, input is required. + /// - NN: The error number, two digits, within C. + + + pub fn internal(&self) -> String { + let ErrCode { public, fatal, category, code, .. } = self.err_code(); + format!("E{}{}{}{}", + public as u16, + fatal as u16, + category as u16, + code) + } +} + impl fmt::Display for Code { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.internal().fmt(f) @@ -56,6 +127,42 @@ impl ErrCode { ErrCode::new(public, fatal, StatusCode::INTERNAL_SERVER_ERROR, category, code) } + pub fn bad_request( + public: bool, + fatal: bool, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode::new(public, fatal, StatusCode::BAD_REQUEST, category, code) + } + + pub fn unsupported_media_type( + public: bool, + fatal: bool, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode::new(public, fatal, StatusCode::UNSUPPORTED_MEDIA_TYPE, category, code) + } + + pub fn payload_too_large( + public: bool, + fatal: bool, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode::new(public, fatal, StatusCode::PAYLOAD_TOO_LARGE, category, code) + } + + pub fn service_unavailable( + public: bool, + fatal: bool, + category: ErrorCategory, + code: u16 + ) -> ErrCode { + ErrCode::new(public, fatal, StatusCode::SERVICE_UNAVAILABLE, category, code) + } + pub fn forbidden( public: bool, fatal: bool, @@ -83,40 +190,3 @@ impl ErrCode { ErrCode::new(public, fatal, StatusCode::NOT_FOUND, category, code) } } - -impl Code { - - /// ascociate a `Code` variant to the actual ErrCode - fn err_code(&self) -> ErrCode { - use Code::*; - - match self { - Other => ErrCode::not_found(false, false, ErrorCategory::None, 0), - } - } - - /// return the HTTP status code ascociated with the `Code` - pub fn http(&self) -> StatusCode { - self.err_code().http_code - } - - /// returns internal error code, in the form: - /// `EPFCNN` - /// - E: plain letter "E", to mark an error code, future main introduce W for warning - /// - P: scope of the error, 0 for private, 1 for public. Private are error that make no sense - /// reporting to the user, they are internal errors, and there is nothing the user can do about - /// them. they are nonetheless returned, without a message, for assistance purpose. - /// - F: 0 or 1, report if the error is fatal. - /// - C: error category, number in 0-9, the category of the error. Categories are still to be determined, input is required. - /// - NN: The error number, two digits, within C. - - - pub fn internal(&self) -> String { - let ErrCode { public, fatal, category, code, .. } = self.err_code(); - format!("E{}{}{}{}", - public as u16, - fatal as u16, - category as u16, - code) - } -} diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index b5dde6f6e..14d5f33e8 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -41,7 +41,6 @@ pub enum Error { MissingHeader(String), NotFound(String), OpenIndex(String), - FilterParsing(String), RetrieveDocument(u32, String), SearchDocuments(String), PayloadTooLarge, @@ -52,8 +51,26 @@ impl error::Error for Error {} impl ErrorCode for Error { fn error_code(&self) -> Code { - //TODO populate with right error codes - Code::Other + use Error::*; + match self { + BadParameter(_, _) => Code::BadParameter, + BadRequest(_) => Code::BadRequest, + CreateIndex(_) => Code::CreateIndex, + DocumentNotFound(_) => Code::DocumentNotFound, + IndexNotFound(_) => Code::IndexNotFound, + Internal(_) => Code::Internal, + InvalidIndexUid => Code::InvalidIndexUid, + InvalidToken(_) => Code::InvalidToken, + Maintenance => Code::Maintenance, + MissingAuthorizationHeader => Code::MissingAuthorizationHeader, + MissingHeader(_) => Code::MissingHeader, + NotFound(_) => Code::NotFound, + OpenIndex(_) => Code::OpenIndex, + RetrieveDocument(_, _) => Code::RetrieveDocument, + SearchDocuments(_) => Code::SearchDocuments, + PayloadTooLarge => Code::PayloadTooLarge, + UnsupportedMediaType => Code::UnsupportedMediaType, + } } } @@ -69,7 +86,7 @@ impl error::Error for FacetCountError {} impl ErrorCode for FacetCountError { fn error_code(&self) -> Code { - unimplemented!() + Code::BadRequest } } @@ -152,7 +169,7 @@ impl Error { Error::Maintenance } - pub fn retrieve_document(doc_id: u64, err: impl fmt::Display) -> Error { + pub fn retrieve_document(doc_id: u32, err: impl fmt::Display) -> Error { Error::RetrieveDocument(doc_id, err.to_string()) } diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 12e4b41f6..c97bc7b28 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -44,10 +44,15 @@ async fn get_document( .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; + let internal_id = index.main .external_to_internal_docid(&reader, &path.document_id)? .ok_or(Error::document_not_found(&path.document_id))?; + let document: Document = index + .document(&reader, None, internal_id)? + .ok_or(Error::document_not_found(&path.document_id))?; + Ok(HttpResponse::Ok().json(document)) } @@ -64,12 +69,13 @@ async fn delete_document( .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let document_id = meilisearch_core::serde::compute_document_id(&path.document_id); - let mut documents_deletion = index.documents_deletion(); documents_deletion.delete_document_by_external_docid(path.document_id.clone()); - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| Ok(documents_deletion.finalize(writer)?))?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = documents_deletion.finalize(writer)?; + Ok(update_id) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index eb7dfbf74..d0114d8c5 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -43,7 +43,7 @@ struct HealthBody { #[put("/health", wrap = "Authentication::Private")] async fn change_healthyness( data: web::Data, - body: web::Json, + body: web::Json, ) -> Result { if body.health { set_healthy(data).await diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index b0ee0e6bf..1a2521386 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -99,7 +99,7 @@ async fn search_with_url_query( let field_ids = prepare_facet_list(&facets, &schema, attrs)?; search_builder.add_facets(field_ids); }, - None => todo!() /* return Err(FacetCountError::NoFacetSet.into()) */ + None => return Err(FacetCountError::NoFacetSet.into()), } } diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 99ea052a9..de4a9fa9e 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -39,12 +39,12 @@ async fn update_all( .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { let settings = body .into_inner() .into_update() .map_err(Error::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -74,10 +74,8 @@ async fn get_all( let mut synonyms = BTreeMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { - let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; - if let Some(list) = alternative_list { - synonyms.insert(synonym, list); - } + let list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; + synonyms.insert(synonym, list); } let ranking_rules = index @@ -209,8 +207,8 @@ async fn update_rules( let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -235,8 +233,8 @@ async fn delete_rules( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -282,8 +280,8 @@ async fn update_distinct( let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -308,8 +306,8 @@ async fn delete_distinct( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -357,8 +355,8 @@ async fn update_searchable( let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -383,8 +381,8 @@ async fn delete_searchable( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -434,8 +432,8 @@ async fn update_displayed( let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -460,8 +458,8 @@ async fn delete_displayed( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -510,8 +508,8 @@ async fn update_accept_new_fields( let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index b420c149b..092ca9b2a 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -49,8 +49,8 @@ async fn update( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -75,8 +75,8 @@ async fn delete( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index c334297af..0b1867126 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -34,10 +34,8 @@ async fn get( let mut synonyms = IndexMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { - let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; - if let Some(list) = alternative_list { - synonyms.insert(synonym, list); - } + let list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; + synonyms.insert(synonym, list); } Ok(HttpResponse::Ok().json(synonyms)) @@ -62,8 +60,8 @@ async fn update( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; @@ -88,8 +86,8 @@ async fn delete( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|mut writer| { - let update_id = index.settings_update(&mut writer, settings)?; + let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { + let update_id = index.settings_update(writer, settings)?; Ok(update_id) })?; diff --git a/meilisearch-http/tests/documents_delete.rs b/meilisearch-http/tests/documents_delete.rs index 48b8f70ee..5aa3e595e 100644 --- a/meilisearch-http/tests/documents_delete.rs +++ b/meilisearch-http/tests/documents_delete.rs @@ -14,7 +14,7 @@ async fn delete() { assert_eq!(status_code, 404); } -// Resolve teh issue https://github.com/meilisearch/MeiliSearch/issues/493 +// Resolve the issue https://github.com/meilisearch/MeiliSearch/issues/493 #[actix_rt::test] async fn delete_batch() { let mut server = common::Server::with_uid("movies"); diff --git a/meilisearch-http/tests/index.rs b/meilisearch-http/tests/index.rs index 6ea81f606..ed67f2d1d 100644 --- a/meilisearch-http/tests/index.rs +++ b/meilisearch-http/tests/index.rs @@ -382,7 +382,7 @@ async fn create_index_failed() { assert_eq!(status_code, 400); let message = res_value["message"].as_str().unwrap(); - assert_eq!(res_value.as_object().unwrap().len(), 1); + assert_eq!(res_value.as_object().unwrap().len(), 4); assert_eq!(message, "Index creation must have an uid"); // 3 - Create a index with extra data @@ -462,7 +462,7 @@ async fn create_index_with_invalid_uid() { assert_eq!(status_code, 400); let message = response["message"].as_str().unwrap(); - assert_eq!(response.as_object().unwrap().len(), 1); + assert_eq!(response.as_object().unwrap().len(), 4); assert_eq!(message, "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."); // 2 - Create the index with invalid uid @@ -475,7 +475,7 @@ async fn create_index_with_invalid_uid() { assert_eq!(status_code, 400); let message = response["message"].as_str().unwrap(); - assert_eq!(response.as_object().unwrap().len(), 1); + assert_eq!(response.as_object().unwrap().len(), 4); assert_eq!(message, "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."); // 3 - Create the index with invalid uid @@ -488,7 +488,7 @@ async fn create_index_with_invalid_uid() { assert_eq!(status_code, 400); let message = response["message"].as_str().unwrap(); - assert_eq!(response.as_object().unwrap().len(), 1); + assert_eq!(response.as_object().unwrap().len(), 4); assert_eq!(message, "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."); // 4 - Create the index with invalid uid @@ -501,7 +501,7 @@ async fn create_index_with_invalid_uid() { assert_eq!(status_code, 400); let message = response["message"].as_str().unwrap(); - assert_eq!(response.as_object().unwrap().len(), 1); + assert_eq!(response.as_object().unwrap().len(), 4); assert_eq!(message, "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."); } @@ -645,12 +645,10 @@ async fn check_add_documents_without_primary_key() { let (response, status_code) = server.add_or_replace_multiple_documents_sync(body).await; - let expected = json!({ - "message": "Could not infer a primary key" - }); - + let message = response["message"].as_str().unwrap(); + assert_eq!(response.as_object().unwrap().len(), 4); + assert_eq!(message, "Could not infer a primary key"); assert_eq!(status_code, 400); - assert_json_eq!(response, expected, ordered: false); } #[actix_rt::test] diff --git a/meilisearch-schema/src/error.rs b/meilisearch-schema/src/error.rs index c1b0ffb3f..84c908596 100644 --- a/meilisearch-schema/src/error.rs +++ b/meilisearch-schema/src/error.rs @@ -26,6 +26,7 @@ impl error::Error for Error {} impl ErrorCode for Error { fn error_code(&self) -> Code { - unimplemented!() + // TODO populate with correct error codes + Code::Internal } } From 30fd24aa471d46da9e9de72588a557e483328c00 Mon Sep 17 00:00:00 2001 From: mpostma Date: Mon, 25 May 2020 11:32:09 +0200 Subject: [PATCH 06/18] fix details --- meilisearch-core/src/database.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index 27b66cdf3..e01a8a95f 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -11,7 +11,7 @@ use heed::{CompactionOption, Result as ZResult}; use log::{debug, error}; use meilisearch_schema::Schema; -use crate::{store, update, Index, MResult, Error, UpdateReader, MainWriter}; +use crate::{store, update, Index, MResult, Error, UpdateReader, UpdateWriter, MainReader, MainWriter}; pub type BoxUpdateFn = Box; type ArcSwapFn = arc_swap::ArcSwapOption; @@ -332,11 +332,11 @@ impl Database { self.update_fn.swap(None); } - pub fn main_read_txn(&self) -> MResult> { + pub fn main_read_txn(&self) -> MResult { Ok(self.env.typed_read_txn::()?) } - pub(crate) fn main_write_txn(&self) -> MResult> { + pub(crate) fn main_write_txn(&self) -> MResult { Ok(self.env.typed_write_txn::()?) } @@ -344,7 +344,7 @@ impl Database { /// transaction is commited. Returns whatever result f returns. pub fn main_write(&self, f: F) -> Result where - F: FnOnce(&mut heed::RwTxn) -> Result, + F: FnOnce(&mut MainWriter) -> Result, E: From, { let mut writer = self.main_write_txn()?; @@ -356,22 +356,26 @@ impl Database { /// provides a context with a reader to the main database. experimental. pub fn main_read(&self, f: F) -> Result where - F: Fn(&heed::RoTxn) -> Result, + F: Fn(&MainReader) -> Result, E: From, { let reader = self.main_read_txn()?; f(&reader) } - pub fn update_read_txn(&self) -> MResult> { + pub fn update_read_txn(&self) -> MResult { Ok(self.update_env.typed_read_txn::()?) } + pub(crate) fn update_write_txn(&self) -> MResult> { + Ok(self.update_env.typed_write_txn::()?) + } + /// Calls f providing it with a writer to the main database. After f is called, makes sure the /// transaction is commited. Returns whatever result f returns. pub fn update_write(&self, f: F) -> Result where - F: FnOnce(&mut heed::RwTxn) -> Result, + F: FnOnce(&mut UpdateWriter) -> Result, E: From, { let mut writer = self.update_write_txn()?; @@ -383,18 +387,14 @@ impl Database { /// provides a context with a reader to the update database. experimental. pub fn update_read(&self, f: F) -> Result where - F: Fn(&heed::RoTxn) -> Result, + F: Fn(&UpdateReader) -> Result, E: From, { let reader = self.update_read_txn()?; f(&reader) } - pub(crate) fn update_write_txn(&self) -> MResult> { - Ok(self.update_env.typed_write_txn::()?) - } - - pub fn copy_and_compact_to_path>(&self, path: P) -> ZResult<(File, File)> { + pub fn copy_and_compact_to_path>(&self, path: P) -> MResult<(File, File)> { let path = path.as_ref(); let env_path = path.join("main"); @@ -411,7 +411,7 @@ impl Database { Ok(update_env_file) => Ok((env_file, update_env_file)), Err(e) => { fs::remove_file(env_path)?; - Err(e) + Err(e.into()) }, } } From 0e20ac28e56727a76143120f522280cbbf0abe88 Mon Sep 17 00:00:00 2001 From: mpostma Date: Mon, 25 May 2020 19:11:38 +0200 Subject: [PATCH 07/18] Change ErrorCategory to ErrorType --- meilisearch-error/src/lib.rs | 189 +++++++++++------------------------ 1 file changed, 61 insertions(+), 128 deletions(-) diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index 59715ef9a..f26cfc042 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -7,8 +7,22 @@ pub trait ErrorCode: std::error::Error { fn error_code(&self) -> Code; } -enum ErrorCategory { - None = 0, +enum ErrorType { + InternalError, + InvalidRequest, + Authentication, +} + +impl fmt::Display for ErrorType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use ErrorType::*; + + match self { + InternalError => write!(f, "internal_error"), + InvalidRequest => write!(f, "invalid_request"), + Authentication => write!(f, "authentication"), + } + } } pub enum Code { @@ -39,154 +53,73 @@ impl Code { use Code::*; match self { - BadParameter - | BadRequest - | CreateIndex - | InvalidIndexUid - | OpenIndex - | RetrieveDocument - | SearchDocuments => ErrCode::bad_request(false, false, ErrorCategory::None, 0), - DocumentNotFound - | IndexNotFound - | NotFound => ErrCode::not_found(false, false, ErrorCategory::None, 0), - InvalidToken - | MissingHeader => ErrCode::unauthorized(false, false, ErrorCategory::None, 0), - MissingAuthorizationHeader => ErrCode::forbidden(false, false, ErrorCategory::None, 0), - Internal => ErrCode::internal(false, false, ErrorCategory::None, 0), - Maintenance => ErrCode::service_unavailable(false, false, ErrorCategory::None, 0), - PayloadTooLarge => ErrCode::payload_too_large(false, false, ErrorCategory::None, 0), - UnsupportedMediaType => ErrCode::unsupported_media_type(false, false, ErrorCategory::None, 0), - _ => ErrCode::not_found(false, false, ErrorCategory::None, 0), + BadParameter => ErrCode::invalid("bad_parameter", StatusCode::BAD_REQUEST), + BadRequest => ErrCode::invalid("bad_request", StatusCode::BAD_REQUEST), + CreateIndex => ErrCode::invalid("create_index", StatusCode::BAD_REQUEST), + InvalidIndexUid => ErrCode::invalid("invalid_index_uid", StatusCode::BAD_REQUEST), + OpenIndex => ErrCode::invalid("open_index", StatusCode::BAD_REQUEST), + RetrieveDocument => ErrCode::invalid("retrieve_document", StatusCode::BAD_REQUEST), + SearchDocuments => ErrCode::invalid("search_document", StatusCode::BAD_REQUEST), + DocumentNotFound => ErrCode::invalid("document_not_found", StatusCode::BAD_REQUEST), + IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::BAD_REQUEST), + NotFound => ErrCode::invalid("not_found", StatusCode::BAD_REQUEST), + InvalidToken => ErrCode::invalid("invalid_token", StatusCode::BAD_REQUEST), + MissingHeader => ErrCode::invalid("missing_header", StatusCode::BAD_REQUEST), + MissingAuthorizationHeader => ErrCode::invalid("missing_authorization_header", StatusCode::BAD_REQUEST), + Internal => ErrCode::invalid("internal", StatusCode::BAD_REQUEST), + Maintenance => ErrCode::invalid("maintenance", StatusCode::BAD_REQUEST), + PayloadTooLarge => ErrCode::invalid("payload_too_large", StatusCode::BAD_REQUEST), + UnsupportedMediaType => ErrCode::invalid("unsupported_media_type", StatusCode::BAD_REQUEST), + _ => ErrCode::invalid("other", StatusCode::BAD_REQUEST), } } /// return the HTTP status code ascociated with the `Code` pub fn http(&self) -> StatusCode { - self.err_code().http_code + self.err_code().status_code } - /// returns internal error code, in the form: - /// `EPFCNN` - /// - E: plain letter "E", to mark an error code, future main introduce W for warning - /// - P: scope of the error, 0 for private, 1 for public. Private are error that make no sense - /// reporting to the user, they are internal errors, and there is nothing the user can do about - /// them. they are nonetheless returned, without a message, for assistance purpose. - /// - F: 0 or 1, report if the error is fatal. - /// - C: error category, number in 0-9, the category of the error. Categories are still to be determined, input is required. - /// - NN: The error number, two digits, within C. - - - pub fn internal(&self) -> String { - let ErrCode { public, fatal, category, code, .. } = self.err_code(); - format!("E{}{}{}{}", - public as u16, - fatal as u16, - category as u16, - code) + /// return error name, used as error code + pub fn name(&self) -> String { + self.err_code().err_name.to_string() } -} -impl fmt::Display for Code { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.internal().fmt(f) + /// return the error type + pub fn r#type(&self) -> String { + self.err_code().err_type.to_string() } } /// Internal structure providing a convenient way to create error codes struct ErrCode { - public: bool, - fatal: bool, - http_code: StatusCode, - category: ErrorCategory, - code: u16, + status_code: StatusCode, + err_type: ErrorType, + err_name: &'static str, } impl ErrCode { - fn new( - public: bool, - fatal: bool, - http_code: StatusCode, - category: ErrorCategory, - code: u16 - ) -> ErrCode { + + fn authentication(err_name: &'static str, status_code: StatusCode) -> ErrCode { ErrCode { - public, - fatal, - http_code, - category, - code, + status_code, + err_name, + err_type: ErrorType::Authentication, } } - pub fn internal( - public: bool, - fatal: bool, - category: ErrorCategory, - code: u16 - ) -> ErrCode { - ErrCode::new(public, fatal, StatusCode::INTERNAL_SERVER_ERROR, category, code) + fn internal(err_name: &'static str, status_code: StatusCode) -> ErrCode { + ErrCode { + status_code, + err_name, + err_type: ErrorType::InternalError, + } } - pub fn bad_request( - public: bool, - fatal: bool, - category: ErrorCategory, - code: u16 - ) -> ErrCode { - ErrCode::new(public, fatal, StatusCode::BAD_REQUEST, category, code) - } - - pub fn unsupported_media_type( - public: bool, - fatal: bool, - category: ErrorCategory, - code: u16 - ) -> ErrCode { - ErrCode::new(public, fatal, StatusCode::UNSUPPORTED_MEDIA_TYPE, category, code) - } - - pub fn payload_too_large( - public: bool, - fatal: bool, - category: ErrorCategory, - code: u16 - ) -> ErrCode { - ErrCode::new(public, fatal, StatusCode::PAYLOAD_TOO_LARGE, category, code) - } - - pub fn service_unavailable( - public: bool, - fatal: bool, - category: ErrorCategory, - code: u16 - ) -> ErrCode { - ErrCode::new(public, fatal, StatusCode::SERVICE_UNAVAILABLE, category, code) - } - - pub fn forbidden( - public: bool, - fatal: bool, - category: ErrorCategory, - code: u16 - ) -> ErrCode { - ErrCode::new(public, fatal, StatusCode::FORBIDDEN, category, code) - } - - pub fn unauthorized( - public: bool, - fatal: bool, - category: ErrorCategory, - code: u16 - ) -> ErrCode { - ErrCode::new(public, fatal, StatusCode::UNAUTHORIZED, category, code) - } - - pub fn not_found( - public: bool, - fatal: bool, - category: ErrorCategory, - code: u16 - ) -> ErrCode { - ErrCode::new(public, fatal, StatusCode::NOT_FOUND, category, code) + fn invalid(err_name: &'static str, status_code: StatusCode) -> ErrCode { + ErrCode { + status_code, + err_name, + err_type: ErrorType::InvalidRequest, + } } } From 9c58ca7ce5911cc88598df0435ede3d44f4f67ae Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 26 May 2020 11:32:03 +0200 Subject: [PATCH 08/18] error codes for core --- meilisearch-core/src/error.rs | 23 +++++++++++++++-- meilisearch-error/src/lib.rs | 47 ++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/meilisearch-core/src/error.rs b/meilisearch-core/src/error.rs index 962468268..076d925ac 100644 --- a/meilisearch-core/src/error.rs +++ b/meilisearch-core/src/error.rs @@ -36,8 +36,27 @@ pub enum Error { impl ErrorCode for Error { fn error_code(&self) -> Code { - //TODO populate codes - Code::Internal + use Error::*; + + match self { + FacetError(_) => Code::Facet, + FilterParseError(_) => Code::Filter, + UnsupportedOperation(_) => Code::BadRequest, + IndexAlreadyExists => Code::IndexAlreadyExists, + MissingPrimaryKey => Code::InvalidState, + MissingDocumentId => Code::MissingDocumentId, + MaxFieldsLimitExceeded => Code::MaxFieldsLimitExceeded, + Schema(s) => s.error_code(), + WordIndexMissing + | SchemaMissing => Code::InvalidState, + Heed(_) + | Fst(_) + | SerdeJson(_) + | Bincode(_) + | Serializer(_) + | Deserializer(_) + | Io(_) => Code::Internal, + } } } diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index f26cfc042..05c0ebb77 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -26,19 +26,33 @@ impl fmt::Display for ErrorType { } pub enum Code { + // index related error + CreateIndex, + IndexAlreadyExists, + IndexNotFound, + InvalidIndexUid, + OpenIndex, + + // invalid state error + InvalidState, + MissingPrimaryKey, + + // invalid documents FIXME make one error code? + MissingDocumentId, + MaxFieldsLimitExceeded, + + Filter, + Facet, + BadParameter, BadRequest, - CreateIndex, DocumentNotFound, - IndexNotFound, Internal, - InvalidIndexUid, InvalidToken, Maintenance, MissingAuthorizationHeader, MissingHeader, NotFound, - OpenIndex, RetrieveDocument, SearchDocuments, PayloadTooLarge, @@ -53,20 +67,35 @@ impl Code { use Code::*; match self { + // index related errors + CreateIndex => ErrCode::invalid("create_index", StatusCode::BAD_REQUEST), + IndexAlreadyExists => ErrCode::invalid("existing_index", StatusCode::BAD_REQUEST), + IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::BAD_REQUEST), + InvalidIndexUid => ErrCode::invalid("invalid_index_uid", StatusCode::BAD_REQUEST), + OpenIndex => ErrCode::internal("open_index", StatusCode::INTERNAL_SERVER_ERROR), + + // invalid state error + InvalidState => ErrCode::internal("invalid_state", StatusCode::INTERNAL_SERVER_ERROR), + // FIXME probably not an internal statuscode there + MissingPrimaryKey => ErrCode::internal("MissingPrimaryKey", StatusCode::INTERNAL_SERVER_ERROR), + + // invalid document + MissingDocumentId => ErrCode::invalid("MissingDocumentId", StatusCode::BAD_REQUEST), + MaxFieldsLimitExceeded => ErrCode::invalid("max_field_limit_exceeded", StatusCode::BAD_REQUEST), + + Filter => ErrCode::invalid("fitler", StatusCode::BAD_REQUEST), + Facet => ErrCode::invalid("fitler", StatusCode::BAD_REQUEST), + BadParameter => ErrCode::invalid("bad_parameter", StatusCode::BAD_REQUEST), BadRequest => ErrCode::invalid("bad_request", StatusCode::BAD_REQUEST), - CreateIndex => ErrCode::invalid("create_index", StatusCode::BAD_REQUEST), - InvalidIndexUid => ErrCode::invalid("invalid_index_uid", StatusCode::BAD_REQUEST), - OpenIndex => ErrCode::invalid("open_index", StatusCode::BAD_REQUEST), RetrieveDocument => ErrCode::invalid("retrieve_document", StatusCode::BAD_REQUEST), SearchDocuments => ErrCode::invalid("search_document", StatusCode::BAD_REQUEST), DocumentNotFound => ErrCode::invalid("document_not_found", StatusCode::BAD_REQUEST), - IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::BAD_REQUEST), NotFound => ErrCode::invalid("not_found", StatusCode::BAD_REQUEST), InvalidToken => ErrCode::invalid("invalid_token", StatusCode::BAD_REQUEST), MissingHeader => ErrCode::invalid("missing_header", StatusCode::BAD_REQUEST), MissingAuthorizationHeader => ErrCode::invalid("missing_authorization_header", StatusCode::BAD_REQUEST), - Internal => ErrCode::invalid("internal", StatusCode::BAD_REQUEST), + Internal => ErrCode::internal("internal", StatusCode::BAD_REQUEST), Maintenance => ErrCode::invalid("maintenance", StatusCode::BAD_REQUEST), PayloadTooLarge => ErrCode::invalid("payload_too_large", StatusCode::BAD_REQUEST), UnsupportedMediaType => ErrCode::invalid("unsupported_media_type", StatusCode::BAD_REQUEST), From e2546f2646c04cacb3255dd6b95773573ffaa8aa Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 26 May 2020 12:17:53 +0200 Subject: [PATCH 09/18] error codes for schema --- meilisearch-error/src/lib.rs | 4 +++- meilisearch-schema/src/error.rs | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index 05c0ebb77..158300499 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -36,6 +36,7 @@ pub enum Code { // invalid state error InvalidState, MissingPrimaryKey, + PrimaryKeyAlreadyPresent, // invalid documents FIXME make one error code? MissingDocumentId, @@ -77,7 +78,8 @@ impl Code { // invalid state error InvalidState => ErrCode::internal("invalid_state", StatusCode::INTERNAL_SERVER_ERROR), // FIXME probably not an internal statuscode there - MissingPrimaryKey => ErrCode::internal("MissingPrimaryKey", StatusCode::INTERNAL_SERVER_ERROR), + MissingPrimaryKey => ErrCode::internal("missing_primary_key", StatusCode::INTERNAL_SERVER_ERROR), + PrimaryKeyAlreadyPresent => ErrCode::internal("primary_key_already_present", StatusCode::INTERNAL_SERVER_ERROR), // invalid document MissingDocumentId => ErrCode::invalid("MissingDocumentId", StatusCode::BAD_REQUEST), diff --git a/meilisearch-schema/src/error.rs b/meilisearch-schema/src/error.rs index 84c908596..8dcc0a7a4 100644 --- a/meilisearch-schema/src/error.rs +++ b/meilisearch-schema/src/error.rs @@ -26,7 +26,12 @@ impl error::Error for Error {} impl ErrorCode for Error { fn error_code(&self) -> Code { - // TODO populate with correct error codes - Code::Internal + use Error::*; + + match self { + FieldNameNotFound(_) => Code::Internal, + MaxFieldsLimitExceeded => Code::MaxFieldsLimitExceeded, + PrimaryKeyAlreadyPresent => Code::PrimaryKeyAlreadyPresent, + } } } From 4254cfbce532f48d794e11f527dd60037ee27a8d Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 26 May 2020 13:41:12 +0200 Subject: [PATCH 10/18] reponse error payload --- meilisearch-http/src/error.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index 14d5f33e8..75567087d 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -204,11 +204,12 @@ impl fmt::Display for Error { impl aweb::error::ResponseError for ResponseError { fn error_response(&self) -> aweb::HttpResponse { - let error_code = self.inner.error_code().internal(); + let error_code = self.inner.error_code(); ResponseBuilder::new(self.status_code()).json(json!({ "message": self.to_string(), - "errorCode": error_code, - "errorLink": format!("docs.meilisearch.come/error/{}", error_code), + "errorCode": error_code.name(), + "errorType": error_code.r#type(), + "errorLink": error_code.url(), })) } From 51b3139c0b023b4e9ba6b82ae5171b16794a0776 Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 26 May 2020 13:41:34 +0200 Subject: [PATCH 11/18] fix status code --- meilisearch-core/src/database.rs | 2 +- meilisearch-error/src/lib.rs | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index e01a8a95f..22c7c7d56 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -7,7 +7,7 @@ use std::{fs, thread}; use chrono::{DateTime, Utc}; use crossbeam_channel::{Receiver, Sender}; use heed::types::{Str, Unit, SerdeBincode}; -use heed::{CompactionOption, Result as ZResult}; +use heed::CompactionOption; use log::{debug, error}; use meilisearch_schema::Schema; diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index 158300499..4afe8a46e 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -71,7 +71,7 @@ impl Code { // index related errors CreateIndex => ErrCode::invalid("create_index", StatusCode::BAD_REQUEST), IndexAlreadyExists => ErrCode::invalid("existing_index", StatusCode::BAD_REQUEST), - IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::BAD_REQUEST), + IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::NOT_FOUND), InvalidIndexUid => ErrCode::invalid("invalid_index_uid", StatusCode::BAD_REQUEST), OpenIndex => ErrCode::internal("open_index", StatusCode::INTERNAL_SERVER_ERROR), @@ -86,21 +86,21 @@ impl Code { MaxFieldsLimitExceeded => ErrCode::invalid("max_field_limit_exceeded", StatusCode::BAD_REQUEST), Filter => ErrCode::invalid("fitler", StatusCode::BAD_REQUEST), - Facet => ErrCode::invalid("fitler", StatusCode::BAD_REQUEST), + Facet => ErrCode::invalid("facet", StatusCode::BAD_REQUEST), BadParameter => ErrCode::invalid("bad_parameter", StatusCode::BAD_REQUEST), BadRequest => ErrCode::invalid("bad_request", StatusCode::BAD_REQUEST), RetrieveDocument => ErrCode::invalid("retrieve_document", StatusCode::BAD_REQUEST), SearchDocuments => ErrCode::invalid("search_document", StatusCode::BAD_REQUEST), - DocumentNotFound => ErrCode::invalid("document_not_found", StatusCode::BAD_REQUEST), - NotFound => ErrCode::invalid("not_found", StatusCode::BAD_REQUEST), - InvalidToken => ErrCode::invalid("invalid_token", StatusCode::BAD_REQUEST), - MissingHeader => ErrCode::invalid("missing_header", StatusCode::BAD_REQUEST), - MissingAuthorizationHeader => ErrCode::invalid("missing_authorization_header", StatusCode::BAD_REQUEST), - Internal => ErrCode::internal("internal", StatusCode::BAD_REQUEST), - Maintenance => ErrCode::invalid("maintenance", StatusCode::BAD_REQUEST), - PayloadTooLarge => ErrCode::invalid("payload_too_large", StatusCode::BAD_REQUEST), - UnsupportedMediaType => ErrCode::invalid("unsupported_media_type", StatusCode::BAD_REQUEST), + DocumentNotFound => ErrCode::invalid("document_not_found", StatusCode::NOT_FOUND), + NotFound => ErrCode::invalid("not_found", StatusCode::NOT_FOUND), + InvalidToken => ErrCode::authentication("invalid_token", StatusCode::UNAUTHORIZED), + MissingHeader => ErrCode::authentication("missing_header", StatusCode::UNAUTHORIZED), + MissingAuthorizationHeader => ErrCode::authentication("missing_authorization_header", StatusCode::FORBIDDEN), + Internal => ErrCode::internal("internal", StatusCode::INTERNAL_SERVER_ERROR), + Maintenance => ErrCode::invalid("maintenance", StatusCode::SERVICE_UNAVAILABLE), + PayloadTooLarge => ErrCode::invalid("payload_too_large", StatusCode::PAYLOAD_TOO_LARGE), + UnsupportedMediaType => ErrCode::invalid("unsupported_media_type", StatusCode::UNSUPPORTED_MEDIA_TYPE), _ => ErrCode::invalid("other", StatusCode::BAD_REQUEST), } } @@ -119,6 +119,10 @@ impl Code { pub fn r#type(&self) -> String { self.err_code().err_type.to_string() } + + pub fn url(&self) -> String { + format!("docs.meilisearch.come/error/{}", self.name()) + } } /// Internal structure providing a convenient way to create error codes From d9e2e1a17750415a3c710d1226327475c1c95a72 Mon Sep 17 00:00:00 2001 From: mpostma Date: Wed, 27 May 2020 10:20:54 +0200 Subject: [PATCH 12/18] ErrorCode improvements --- meilisearch-error/src/lib.rs | 32 ++++++++++++++++++++++++++------ meilisearch-http/src/error.rs | 17 ++++++++++++----- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index 4afe8a46e..63f94f655 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -1,10 +1,29 @@ -#![allow(dead_code)] - use std::fmt; + use actix_http::http::StatusCode; pub trait ErrorCode: std::error::Error { fn error_code(&self) -> Code; + + /// returns the HTTP status code ascociated with the error + fn http_status(&self) -> StatusCode { + self.error_code().http() + } + + /// returns the doc url ascociated with the error + fn error_url(&self) -> String { + self.error_code().url() + } + + /// returns error name, used as error code + fn error_name(&self) -> String { + self.error_code().name() + } + + /// return the error type + fn error_type(&self) -> String { + self.error_code().r#type() + } } enum ErrorType { @@ -106,21 +125,22 @@ impl Code { } /// return the HTTP status code ascociated with the `Code` - pub fn http(&self) -> StatusCode { + fn http(&self) -> StatusCode { self.err_code().status_code } /// return error name, used as error code - pub fn name(&self) -> String { + fn name(&self) -> String { self.err_code().err_name.to_string() } /// return the error type - pub fn r#type(&self) -> String { + fn r#type(&self) -> String { self.err_code().err_type.to_string() } - pub fn url(&self) -> String { + /// return the doc url ascociated with the error + fn url(&self) -> String { format!("docs.meilisearch.come/error/{}", self.name()) } } diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index 75567087d..606b54b65 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -14,6 +14,14 @@ pub struct ResponseError { inner: Box, } +impl error::Error for ResponseError {} + +impl ErrorCode for ResponseError { + fn error_code(&self) -> Code { + self.inner.error_code() + } +} + impl fmt::Display for ResponseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) @@ -204,17 +212,16 @@ impl fmt::Display for Error { impl aweb::error::ResponseError for ResponseError { fn error_response(&self) -> aweb::HttpResponse { - let error_code = self.inner.error_code(); ResponseBuilder::new(self.status_code()).json(json!({ "message": self.to_string(), - "errorCode": error_code.name(), - "errorType": error_code.r#type(), - "errorLink": error_code.url(), + "errorCode": self.error_name(), + "errorType": self.error_type(), + "errorLink": self.error_url(), })) } fn status_code(&self) -> StatusCode { - self.inner.error_code().http() + self.http_status() } } From 05344043b238865f9d786d63ae8a10139f34e4ae Mon Sep 17 00:00:00 2001 From: mpostma Date: Wed, 27 May 2020 12:04:35 +0200 Subject: [PATCH 13/18] style fixes --- meilisearch-core/src/database.rs | 15 ++++--- meilisearch-core/src/lib.rs | 8 +--- meilisearch-core/src/store/main.rs | 6 +-- meilisearch-core/src/store/synonyms.rs | 3 +- meilisearch-error/Cargo.toml | 4 +- meilisearch-error/src/lib.rs | 52 +++++++++++------------ meilisearch-http/src/data.rs | 1 - meilisearch-http/src/error.rs | 4 +- meilisearch-http/src/routes/document.rs | 4 +- meilisearch-http/src/routes/health.rs | 2 + meilisearch-http/src/routes/setting.rs | 4 +- meilisearch-http/src/routes/stop_words.rs | 2 +- meilisearch-http/src/routes/synonym.rs | 2 +- 13 files changed, 51 insertions(+), 56 deletions(-) diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index 22c7c7d56..0b0cd3149 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -11,13 +11,20 @@ use heed::CompactionOption; use log::{debug, error}; use meilisearch_schema::Schema; -use crate::{store, update, Index, MResult, Error, UpdateReader, UpdateWriter, MainReader, MainWriter}; +use crate::{store, update, Index, MResult, Error}; pub type BoxUpdateFn = Box; + type ArcSwapFn = arc_swap::ArcSwapOption; type SerdeDatetime = SerdeBincode>; +pub type MainWriter<'a> = heed::RwTxn<'a, MainT>; +pub type MainReader = heed::RoTxn; + +pub type UpdateWriter<'a> = heed::RwTxn<'a, UpdateT>; +pub type UpdateReader = heed::RoTxn; + const UNHEALTHY_KEY: &str = "_is_unhealthy"; const LAST_UPDATE_KEY: &str = "last-update"; @@ -427,8 +434,7 @@ impl Database { pub fn last_update(&self, reader: &heed::RoTxn) -> MResult>> { match self.common_store() - .get::<_, Str, SerdeDatetime>(reader, LAST_UPDATE_KEY)? - { + .get::<_, Str, SerdeDatetime>(reader, LAST_UPDATE_KEY)? { Some(datetime) => Ok(Some(datetime)), None => Ok(None), } @@ -492,7 +498,7 @@ impl Database { index .main - .put_fields_frequency(writer, &frequency) + .put_fields_distribution(writer, &frequency) } } @@ -1223,4 +1229,3 @@ mod tests { assert_matches!(iter.next(), None); } } - diff --git a/meilisearch-core/src/lib.rs b/meilisearch-core/src/lib.rs index d837bae62..e468a794e 100644 --- a/meilisearch-core/src/lib.rs +++ b/meilisearch-core/src/lib.rs @@ -26,7 +26,7 @@ pub mod settings; pub mod store; pub mod update; -pub use self::database::{BoxUpdateFn, Database, DatabaseOptions, MainT, UpdateT}; +pub use self::database::{BoxUpdateFn, Database, DatabaseOptions, MainT, UpdateT, MainWriter, MainReader, UpdateWriter, UpdateReader}; pub use self::error::{Error, HeedError, FstError, MResult, pest_error, FacetError}; pub use self::filters::Filter; pub use self::number::{Number, ParseNumberError}; @@ -52,12 +52,6 @@ use crate::reordered_attrs::ReorderedAttrs; type FstSetCow<'a> = fst::Set>; type FstMapCow<'a> = fst::Map>; -pub type MainWriter<'a> = heed::RwTxn<'a, MainT>; -pub type MainReader = heed::RoTxn; - -pub type UpdateWriter<'a> = heed::RwTxn<'a, UpdateT>; -pub type UpdateReader = heed::RoTxn; - #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Document { pub id: DocumentId, diff --git a/meilisearch-core/src/store/main.rs b/meilisearch-core/src/store/main.rs index 21b506307..f1ef6fc5e 100644 --- a/meilisearch-core/src/store/main.rs +++ b/meilisearch-core/src/store/main.rs @@ -197,7 +197,7 @@ impl Main { } } - pub fn synonyms_list(self, reader: &heed::RoTxn) -> MResult> { + pub fn synonyms(self, reader: &heed::RoTxn) -> MResult> { let synonyms = self .synonyms_fst(&reader)? .stream() @@ -217,7 +217,7 @@ impl Main { } } - pub fn stop_words_list(self, reader: &heed::RoTxn) -> MResult> { + pub fn stop_words(self, reader: &heed::RoTxn) -> MResult> { let stop_word_list = self .stop_words_fst(reader)? .stream() @@ -249,7 +249,7 @@ impl Main { writer: &mut heed::RwTxn, fields_frequency: &FreqsMap, ) -> MResult<()> { - Ok(self.main.put::<_, Str, SerdeFreqsMap>(writer, FIELDS_FREQUENCY_KEY, fields_frequency)?) + Ok(self.main.put::<_, Str, SerdeFreqsMap>(writer, FIELDS_DISTRIBUTION_KEY, fields_frequency)?) } pub fn fields_distribution(&self, reader: &heed::RoTxn) -> MResult> { diff --git a/meilisearch-core/src/store/synonyms.rs b/meilisearch-core/src/store/synonyms.rs index 699667fcd..bf7472f96 100644 --- a/meilisearch-core/src/store/synonyms.rs +++ b/meilisearch-core/src/store/synonyms.rs @@ -27,7 +27,7 @@ impl Synonyms { self.synonyms.clear(writer) } - pub fn synonyms_fst<'txn>(self, reader: &'txn heed::RoTxn, word: &[u8]) -> ZResult> { + pub(crate) fn synonyms_fst<'txn>(self, reader: &'txn heed::RoTxn, word: &[u8]) -> ZResult> { match self.synonyms.get(reader, word)? { Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()), None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()), @@ -42,4 +42,3 @@ impl Synonyms { Ok(synonyms) } } - diff --git a/meilisearch-error/Cargo.toml b/meilisearch-error/Cargo.toml index 1be97bdb8..ad77978b1 100644 --- a/meilisearch-error/Cargo.toml +++ b/meilisearch-error/Cargo.toml @@ -1,10 +1,8 @@ [package] name = "meilisearch-error" version = "0.10.1" -authors = ["mpostma "] +authors = ["marin "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] actix-http = "1.0.1" diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index 63f94f655..66838ed02 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -57,12 +57,11 @@ pub enum Code { MissingPrimaryKey, PrimaryKeyAlreadyPresent, - // invalid documents FIXME make one error code? - MissingDocumentId, MaxFieldsLimitExceeded, + MissingDocumentId, - Filter, Facet, + Filter, BadParameter, BadRequest, @@ -73,9 +72,9 @@ pub enum Code { MissingAuthorizationHeader, MissingHeader, NotFound, + PayloadTooLarge, RetrieveDocument, SearchDocuments, - PayloadTooLarge, UnsupportedMediaType, Other, } @@ -90,8 +89,7 @@ impl Code { // index related errors CreateIndex => ErrCode::invalid("create_index", StatusCode::BAD_REQUEST), IndexAlreadyExists => ErrCode::invalid("existing_index", StatusCode::BAD_REQUEST), - IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::NOT_FOUND), - InvalidIndexUid => ErrCode::invalid("invalid_index_uid", StatusCode::BAD_REQUEST), + IndexNotFound => ErrCode::invalid("index_not_found", StatusCode::NOT_FOUND), InvalidIndexUid => ErrCode::invalid("invalid_index_uid", StatusCode::BAD_REQUEST), OpenIndex => ErrCode::internal("open_index", StatusCode::INTERNAL_SERVER_ERROR), // invalid state error @@ -101,24 +99,24 @@ impl Code { PrimaryKeyAlreadyPresent => ErrCode::internal("primary_key_already_present", StatusCode::INTERNAL_SERVER_ERROR), // invalid document - MissingDocumentId => ErrCode::invalid("MissingDocumentId", StatusCode::BAD_REQUEST), MaxFieldsLimitExceeded => ErrCode::invalid("max_field_limit_exceeded", StatusCode::BAD_REQUEST), + MissingDocumentId => ErrCode::invalid("missing_document_id", StatusCode::BAD_REQUEST), - Filter => ErrCode::invalid("fitler", StatusCode::BAD_REQUEST), - Facet => ErrCode::invalid("facet", StatusCode::BAD_REQUEST), + Facet => ErrCode::invalid("invalid_facet", StatusCode::BAD_REQUEST), + Filter => ErrCode::invalid("invalid_filter", StatusCode::BAD_REQUEST), BadParameter => ErrCode::invalid("bad_parameter", StatusCode::BAD_REQUEST), BadRequest => ErrCode::invalid("bad_request", StatusCode::BAD_REQUEST), - RetrieveDocument => ErrCode::invalid("retrieve_document", StatusCode::BAD_REQUEST), - SearchDocuments => ErrCode::invalid("search_document", StatusCode::BAD_REQUEST), - DocumentNotFound => ErrCode::invalid("document_not_found", StatusCode::NOT_FOUND), - NotFound => ErrCode::invalid("not_found", StatusCode::NOT_FOUND), - InvalidToken => ErrCode::authentication("invalid_token", StatusCode::UNAUTHORIZED), - MissingHeader => ErrCode::authentication("missing_header", StatusCode::UNAUTHORIZED), - MissingAuthorizationHeader => ErrCode::authentication("missing_authorization_header", StatusCode::FORBIDDEN), + DocumentNotFound => ErrCode::internal("document_not_found", StatusCode::NOT_FOUND), Internal => ErrCode::internal("internal", StatusCode::INTERNAL_SERVER_ERROR), - Maintenance => ErrCode::invalid("maintenance", StatusCode::SERVICE_UNAVAILABLE), + InvalidToken => ErrCode::authentication("invalid_token", StatusCode::UNAUTHORIZED), + Maintenance => ErrCode::internal("maintenance", StatusCode::SERVICE_UNAVAILABLE), + MissingAuthorizationHeader => ErrCode::authentication("missing_authorization_header", StatusCode::FORBIDDEN), + MissingHeader => ErrCode::authentication("missing_header", StatusCode::UNAUTHORIZED), + NotFound => ErrCode::invalid("not_found", StatusCode::NOT_FOUND), PayloadTooLarge => ErrCode::invalid("payload_too_large", StatusCode::PAYLOAD_TOO_LARGE), + RetrieveDocument => ErrCode::internal("retrieve_document", StatusCode::BAD_REQUEST), + SearchDocuments => ErrCode::internal("search_error", StatusCode::BAD_REQUEST), UnsupportedMediaType => ErrCode::invalid("unsupported_media_type", StatusCode::UNSUPPORTED_MEDIA_TYPE), _ => ErrCode::invalid("other", StatusCode::BAD_REQUEST), } @@ -131,12 +129,12 @@ impl Code { /// return error name, used as error code fn name(&self) -> String { - self.err_code().err_name.to_string() + self.err_code().error_name.to_string() } /// return the error type fn r#type(&self) -> String { - self.err_code().err_type.to_string() + self.err_code().error_type.to_string() } /// return the doc url ascociated with the error @@ -148,8 +146,8 @@ impl Code { /// Internal structure providing a convenient way to create error codes struct ErrCode { status_code: StatusCode, - err_type: ErrorType, - err_name: &'static str, + error_type: ErrorType, + error_name: &'static str, } impl ErrCode { @@ -157,24 +155,24 @@ impl ErrCode { fn authentication(err_name: &'static str, status_code: StatusCode) -> ErrCode { ErrCode { status_code, - err_name, - err_type: ErrorType::Authentication, + error_name: err_name, + error_type: ErrorType::Authentication, } } fn internal(err_name: &'static str, status_code: StatusCode) -> ErrCode { ErrCode { status_code, - err_name, - err_type: ErrorType::InternalError, + error_name: err_name, + error_type: ErrorType::InternalError, } } fn invalid(err_name: &'static str, status_code: StatusCode) -> ErrCode { ErrCode { status_code, - err_name, - err_type: ErrorType::InvalidRequest, + error_name: err_name, + error_type: ErrorType::InvalidRequest, } } } diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index 15c2ff1af..3692b0b69 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -96,4 +96,3 @@ impl Data { data } } - diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index 606b54b65..3f57e3a92 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -1,11 +1,11 @@ -use std::fmt; use std::error; +use std::fmt; use actix_http::ResponseBuilder; use actix_web as aweb; +use actix_web::error::JsonPayloadError; use actix_web::http::StatusCode; use serde_json::json; -use actix_web::error::JsonPayloadError; use meilisearch_error::{ErrorCode, Code}; diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index c97bc7b28..448d04562 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -7,10 +7,10 @@ use meilisearch_core::update; use serde::Deserialize; use serde_json::Value; +use crate::Data; use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; -use crate::Data; type Document = IndexMap; @@ -192,7 +192,7 @@ async fn update_multiple_documents( let update_id = data .db - .update_write::<_, _, ResponseError>(move |writer| { + .update_write::<_, _, ResponseError>(|writer| { let update_id = document_addition.finalize(writer)?; Ok(update_id) })?; diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index d0114d8c5..c6f33b6fb 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -24,6 +24,7 @@ async fn set_healthy(data: web::Data) -> Result) -> Result = index .main - .stop_words_list(&reader)? + .stop_words(&reader)? .into_iter() .collect(); - let synonyms_list = index.main.synonyms_list(&reader)?; + let synonyms_list = index.main.synonyms(&reader)?; let mut synonyms = BTreeMap::new(); let index_synonyms = &index.synonyms; diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index 092ca9b2a..0c22cbd13 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -25,7 +25,7 @@ async fn get( .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; - let stop_words = index.main.stop_words_list(&reader)?; + let stop_words = index.main.stop_words(&reader)?; Ok(HttpResponse::Ok().json(stop_words)) } diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index 0b1867126..169230adb 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -29,7 +29,7 @@ async fn get( let reader = data.db.main_read_txn()?; - let synonyms_list = index.main.synonyms_list(&reader)?; + let synonyms_list = index.main.synonyms(&reader)?; let mut synonyms = IndexMap::new(); let index_synonyms = &index.synonyms; From fb6a9ea280b053451c4d4adaa8ab2415ca15af93 Mon Sep 17 00:00:00 2001 From: mpostma Date: Wed, 27 May 2020 11:56:14 +0200 Subject: [PATCH 14/18] remove unecessary errors --- meilisearch-core/src/error.rs | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/meilisearch-core/src/error.rs b/meilisearch-core/src/error.rs index 076d925ac..de0391bce 100644 --- a/meilisearch-core/src/error.rs +++ b/meilisearch-core/src/error.rs @@ -29,7 +29,6 @@ pub enum Error { Bincode(bincode::Error), Serializer(SerializerError), Deserializer(DeserializerError), - UnsupportedOperation(UnsupportedOperation), FilterParseError(PestError), FacetError(FacetError), } @@ -41,7 +40,6 @@ impl ErrorCode for Error { match self { FacetError(_) => Code::Facet, FilterParseError(_) => Code::Filter, - UnsupportedOperation(_) => Code::BadRequest, IndexAlreadyExists => Code::IndexAlreadyExists, MissingPrimaryKey => Code::InvalidState, MissingDocumentId => Code::MissingDocumentId, @@ -136,12 +134,6 @@ impl From for Error { } } -impl From for Error { - fn from(op: UnsupportedOperation) -> Error { - Error::UnsupportedOperation(op) - } -} - impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; @@ -160,7 +152,6 @@ impl fmt::Display for Error { Bincode(e) => write!(f, "bincode error; {}", e), Serializer(e) => write!(f, "serializer error; {}", e), Deserializer(e) => write!(f, "deserializer error; {}", e), - UnsupportedOperation(op) => write!(f, "unsupported operation; {}", op), FilterParseError(e) => write!(f, "error parsing filter; {}", e), FacetError(e) => write!(f, "error processing facet filter: {}", e), } @@ -169,30 +160,6 @@ impl fmt::Display for Error { impl error::Error for Error {} -#[derive(Debug)] -pub enum UnsupportedOperation { - SchemaAlreadyExists, - CannotUpdateSchemaPrimaryKey, - CannotReorderSchemaAttribute, - CanOnlyIntroduceNewSchemaAttributesAtEnd, - CannotRemoveSchemaAttribute, -} - -impl fmt::Display for UnsupportedOperation { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::UnsupportedOperation::*; - match self { - SchemaAlreadyExists => write!(f, "Cannot update index which already have a schema"), - CannotUpdateSchemaPrimaryKey => write!(f, "Cannot update the primary key of a schema"), - CannotReorderSchemaAttribute => write!(f, "Cannot reorder the attributes of a schema"), - CanOnlyIntroduceNewSchemaAttributesAtEnd => { - write!(f, "Can only introduce new attributes at end of a schema") - } - CannotRemoveSchemaAttribute => write!(f, "Cannot remove attributes from a schema"), - } - } -} - #[derive(Debug)] pub enum FacetError { EmptyArray, @@ -202,7 +169,6 @@ pub enum FacetError { AttributeNotFound(String), AttributeNotSet { expected: Vec, found: String }, InvalidDocumentAttribute(String), - NoFacetAttributes, } impl FacetError { @@ -227,7 +193,6 @@ impl fmt::Display for FacetError { AttributeNotFound(attr) => write!(f, "unknown {:?} attribute", attr), AttributeNotSet { found, expected } => write!(f, "`{}` is not set as a faceted attribute. available facet attributes: {}", found, expected.join(", ")), InvalidDocumentAttribute(attr) => write!(f, "invalid document attribute {}, accepted types: String and [String]", attr), - NoFacetAttributes => write!(f, "No attributes are set for faceting"), } } } From c9f10432b80f728dfba6da806ef113446346a503 Mon Sep 17 00:00:00 2001 From: mpostma Date: Wed, 27 May 2020 12:10:41 +0200 Subject: [PATCH 15/18] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2cbc38b0..7144ab2f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## v0.10.2 - - Change the HTTP framework, moving from tide to actix-web #601 + - Change the HTTP framework, moving from tide to actix-web (#601) - Bump sentry version to 0.18.1 (#690) - Enable max payload size override (#684) - Disable sentry in debug (#681) @@ -18,6 +18,7 @@ - Update sentry features to remove openssl (#702) - Add SSL support (#669) - Rename fieldsFrequency into fieldsDistribution in stats (#719) + - Add support for error code reporting (#703) ## v0.10.1 From 2bf82b31981a5e76f6a200ec06f441df74abc4d6 Mon Sep 17 00:00:00 2001 From: mpostma Date: Wed, 27 May 2020 14:21:00 +0200 Subject: [PATCH 16/18] update error codes --- meilisearch-error/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index 66838ed02..b5dce37e8 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -76,7 +76,6 @@ pub enum Code { RetrieveDocument, SearchDocuments, UnsupportedMediaType, - Other, } impl Code { @@ -94,7 +93,6 @@ impl Code { // invalid state error InvalidState => ErrCode::internal("invalid_state", StatusCode::INTERNAL_SERVER_ERROR), - // FIXME probably not an internal statuscode there MissingPrimaryKey => ErrCode::internal("missing_primary_key", StatusCode::INTERNAL_SERVER_ERROR), PrimaryKeyAlreadyPresent => ErrCode::internal("primary_key_already_present", StatusCode::INTERNAL_SERVER_ERROR), @@ -118,7 +116,6 @@ impl Code { RetrieveDocument => ErrCode::internal("retrieve_document", StatusCode::BAD_REQUEST), SearchDocuments => ErrCode::internal("search_error", StatusCode::BAD_REQUEST), UnsupportedMediaType => ErrCode::invalid("unsupported_media_type", StatusCode::UNSUPPORTED_MEDIA_TYPE), - _ => ErrCode::invalid("other", StatusCode::BAD_REQUEST), } } @@ -139,7 +136,7 @@ impl Code { /// return the doc url ascociated with the error fn url(&self) -> String { - format!("docs.meilisearch.come/error/{}", self.name()) + format!("docs.meilisearch.com/error/{}", self.name()) } } @@ -151,7 +148,6 @@ struct ErrCode { } impl ErrCode { - fn authentication(err_name: &'static str, status_code: StatusCode) -> ErrCode { ErrCode { status_code, From 2a32ad39a09b8848d485577379e4c8a2cec4cdc5 Mon Sep 17 00:00:00 2001 From: mpostma Date: Wed, 27 May 2020 17:43:37 +0200 Subject: [PATCH 17/18] move filter parse error display to core --- meilisearch-core/src/error.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/meilisearch-core/src/error.rs b/meilisearch-core/src/error.rs index de0391bce..62840c2a8 100644 --- a/meilisearch-core/src/error.rs +++ b/meilisearch-core/src/error.rs @@ -160,6 +160,20 @@ impl fmt::Display for Error { impl error::Error for Error {} +struct FilterParseError(PestError); + +impl fmt::Display for FilterParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use crate::pest_error::LineColLocation::*; + + let (line, column) = match self.0.line_col { + Span((line, _), (column, _)) => (line, column), + Pos((line, column)) => (line, column), + }; + write!(f, "parsing error on line {} at column {}: {}", line, column, self.0.variant.message()) + } +} + #[derive(Debug)] pub enum FacetError { EmptyArray, From 8a2e60dc096617e5c1df8b142667b348a7b823e3 Mon Sep 17 00:00:00 2001 From: mpostma Date: Thu, 28 May 2020 16:12:24 +0200 Subject: [PATCH 18/18] requested changes --- meilisearch-core/examples/from_file.rs | 12 ++--- meilisearch-core/src/database.rs | 12 +++-- meilisearch-error/src/lib.rs | 18 ++++---- meilisearch-http/src/routes/document.rs | 21 ++------- meilisearch-http/src/routes/health.rs | 12 +---- meilisearch-http/src/routes/index.rs | 10 ++-- meilisearch-http/src/routes/setting.rs | 54 ++++------------------ meilisearch-http/src/routes/stop_words.rs | 10 +--- meilisearch-http/src/routes/synonym.rs | 10 +--- meilisearch-http/tests/documents_delete.rs | 1 - 10 files changed, 46 insertions(+), 114 deletions(-) diff --git a/meilisearch-core/examples/from_file.rs b/meilisearch-core/examples/from_file.rs index 561036c02..af41efb6c 100644 --- a/meilisearch-core/examples/from_file.rs +++ b/meilisearch-core/examples/from_file.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use structopt::StructOpt; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; -use meilisearch_core::{Database, DatabaseOptions, Highlight, ProcessedUpdateResult, Error as MError}; +use meilisearch_core::{Database, DatabaseOptions, Highlight, ProcessedUpdateResult}; use meilisearch_core::settings::Settings; use meilisearch_schema::FieldId; @@ -126,10 +126,7 @@ fn index_command(command: IndexCommand, database: Database) -> Result<(), Box(|writer| { - index.settings_update(writer, settings)?; - Ok(()) - })?; + db.update_write(|w| index.settings_update(w, settings))?; let mut rdr = if command.csv_data_path.as_os_str() == "-" { csv::Reader::from_reader(Box::new(io::stdin()) as Box) @@ -176,10 +173,7 @@ fn index_command(command: IndexCommand, database: Database) -> Result<(), Box(|writer| { - let update_id = additions.finalize(writer)?; - Ok(update_id) - })?; + let update_id = db.update_write(|w| additions.finalize(w))?; println!("committing update..."); max_update_id = max_update_id.max(update_id); diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index 0b0cd3149..79a1e430c 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -363,11 +363,13 @@ impl Database { /// provides a context with a reader to the main database. experimental. pub fn main_read(&self, f: F) -> Result where - F: Fn(&MainReader) -> Result, + F: FnOnce(&MainReader) -> Result, E: From, { let reader = self.main_read_txn()?; - f(&reader) + let result = f(&reader)?; + reader.abort().map_err(Error::Heed)?; + Ok(result) } pub fn update_read_txn(&self) -> MResult { @@ -394,11 +396,13 @@ impl Database { /// provides a context with a reader to the update database. experimental. pub fn update_read(&self, f: F) -> Result where - F: Fn(&UpdateReader) -> Result, + F: FnOnce(&UpdateReader) -> Result, E: From, { let reader = self.update_read_txn()?; - f(&reader) + let result = f(&reader)?; + reader.abort().map_err(Error::Heed)?; + Ok(result) } pub fn copy_and_compact_to_path>(&self, path: P) -> MResult<(File, File)> { diff --git a/meilisearch-error/src/lib.rs b/meilisearch-error/src/lib.rs index b5dce37e8..1c8fc4e78 100644 --- a/meilisearch-error/src/lib.rs +++ b/meilisearch-error/src/lib.rs @@ -22,7 +22,7 @@ pub trait ErrorCode: std::error::Error { /// return the error type fn error_type(&self) -> String { - self.error_code().r#type() + self.error_code().type_() } } @@ -130,13 +130,13 @@ impl Code { } /// return the error type - fn r#type(&self) -> String { + fn type_(&self) -> String { self.err_code().error_type.to_string() } /// return the doc url ascociated with the error fn url(&self) -> String { - format!("docs.meilisearch.com/error/{}", self.name()) + format!("https://docs.meilisearch.com/error/{}", self.name()) } } @@ -148,26 +148,26 @@ struct ErrCode { } impl ErrCode { - fn authentication(err_name: &'static str, status_code: StatusCode) -> ErrCode { + fn authentication(error_name: &'static str, status_code: StatusCode) -> ErrCode { ErrCode { status_code, - error_name: err_name, + error_name, error_type: ErrorType::Authentication, } } - fn internal(err_name: &'static str, status_code: StatusCode) -> ErrCode { + fn internal(error_name: &'static str, status_code: StatusCode) -> ErrCode { ErrCode { status_code, - error_name: err_name, + error_name, error_type: ErrorType::InternalError, } } - fn invalid(err_name: &'static str, status_code: StatusCode) -> ErrCode { + fn invalid(error_name: &'static str, status_code: StatusCode) -> ErrCode { ErrCode { status_code, - error_name: err_name, + error_name, error_type: ErrorType::InvalidRequest, } } diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 448d04562..e7ad3801b 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -72,10 +72,7 @@ async fn delete_document( let mut documents_deletion = index.documents_deletion(); documents_deletion.delete_document_by_external_docid(path.document_id.clone()); - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = documents_deletion.finalize(writer)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| documents_deletion.finalize(w))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -174,10 +171,7 @@ async fn update_multiple_documents( .set_primary_key(&id) .map_err(Error::bad_request)?; - data.db.main_write::<_, _, ResponseError>(|mut writer| { - index.main.put_schema(&mut writer, &schema)?; - Ok(()) - })?; + data.db.main_write(|w| index.main.put_schema(w, &schema))?; } let mut document_addition = if is_partial { @@ -190,12 +184,7 @@ async fn update_multiple_documents( document_addition.update_document(document); } - let update_id = data - .db - .update_write::<_, _, ResponseError>(|writer| { - let update_id = document_addition.finalize(writer)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| document_addition.finalize(w))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -242,7 +231,7 @@ async fn delete_documents( documents_deletion.delete_document_by_external_docid(document_id); } - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| Ok(documents_deletion.finalize(writer)?))?; + let update_id = data.db.update_write(|w| documents_deletion.finalize(w))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -257,7 +246,7 @@ async fn clear_all_documents( .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| Ok(index.clear_all(writer)?))?; + let update_id = data.db.update_write(|w| index.clear_all(w))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index c6f33b6fb..0f0a3a203 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -20,20 +20,12 @@ async fn get_health(data: web::Data) -> Result) -> Result { - data.db.main_write::<_, _, ResponseError>(|writer| { - data.db.set_healthy(writer)?; - Ok(()) - })?; - + data.db.main_write(|w| data.db.set_healthy(w))?; Ok(HttpResponse::Ok().finish()) } async fn set_unhealthy(data: web::Data) -> Result { - data.db.main_write::<_, _, ResponseError>(|writer| { - data.db.set_unhealthy(writer)?; - Ok(()) - })?; - + data.db.main_write(|w| data.db.set_unhealthy(w))?; Ok(HttpResponse::Ok().finish()) } diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index 6d8d654a0..f12179902 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -243,13 +243,13 @@ async fn update_index( .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - data.db.main_write::<_, _, ResponseError>(|mut writer| { + data.db.main_write::<_, _, ResponseError>(|writer| { if let Some(name) = &body.name { - index.main.put_name(&mut writer, name)?; + index.main.put_name(writer, name)?; } if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = index.main.schema(&writer)? { + if let Some(mut schema) = index.main.schema(writer)? { match schema.primary_key() { Some(_) => { return Err(Error::bad_request( @@ -258,12 +258,12 @@ async fn update_index( } None => { schema.set_primary_key(&id)?; - index.main.put_schema(&mut writer, &schema)?; + index.main.put_schema(writer, &schema)?; } } } } - index.main.put_updated_at(&mut writer)?; + index.main.put_updated_at(writer)?; Ok(()) })?; diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 6bb883ee8..c7abd6ce4 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -153,10 +153,7 @@ async fn delete_all( attributes_for_faceting: UpdateState::Clear, }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -206,11 +203,7 @@ async fn update_rules( }; let settings = settings.into_update().map_err(Error::bad_request)?; - - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -233,10 +226,7 @@ async fn delete_rules( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -279,11 +269,7 @@ async fn update_distinct( }; let settings = settings.into_update().map_err(Error::bad_request)?; - - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -306,10 +292,7 @@ async fn delete_distinct( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -355,10 +338,7 @@ async fn update_searchable( let settings = settings.into_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -381,10 +361,7 @@ async fn delete_searchable( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -431,11 +408,7 @@ async fn update_displayed( }; let settings = settings.into_update().map_err(Error::bad_request)?; - - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -458,10 +431,7 @@ async fn delete_displayed( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -507,11 +477,7 @@ async fn update_accept_new_fields( }; let settings = settings.into_update().map_err(Error::bad_request)?; - - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index 0c22cbd13..eaea22827 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -49,10 +49,7 @@ async fn update( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -75,10 +72,7 @@ async fn delete( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index 169230adb..f047e9dd3 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -60,10 +60,7 @@ async fn update( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -86,10 +83,7 @@ async fn delete( ..SettingsUpdate::default() }; - let update_id = data.db.update_write::<_, _, ResponseError>(|writer| { - let update_id = index.settings_update(writer, settings)?; - Ok(update_id) - })?; + let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/tests/documents_delete.rs b/meilisearch-http/tests/documents_delete.rs index 5aa3e595e..cd0bfc142 100644 --- a/meilisearch-http/tests/documents_delete.rs +++ b/meilisearch-http/tests/documents_delete.rs @@ -21,7 +21,6 @@ async fn delete_batch() { server.populate_movies().await; let (_response, status_code) = server.get_document(419704).await; - println!("{:?}", _response); assert_eq!(status_code, 200); let body = serde_json::json!([419704, 512200, 181812]);