simplify error handling by impl errors traits on ResponseError

This commit is contained in:
Quentin de Quelen 2020-04-17 14:52:13 +02:00 committed by qdequele
parent 4bd7e46ba6
commit e74d2c1872
No known key found for this signature in database
GPG Key ID: B3F0A000EBF11745
10 changed files with 470 additions and 807 deletions

View File

@ -7,39 +7,109 @@ use serde_json::json;
#[derive(Debug)]
pub enum ResponseError {
Internal(String),
BadRequest(String),
MissingAuthorizationHeader,
InvalidToken(String),
NotFound(String),
IndexNotFound(String),
DocumentNotFound(String),
MissingHeader(String),
FilterParsing(String),
BadParameter(String, String),
OpenIndex(String),
BadRequest(String),
CreateIndex(String),
DocumentNotFound(String),
IndexNotFound(String),
Internal(String),
InvalidIndexUid,
InvalidToken(String),
Maintenance,
MissingAuthorizationHeader,
MissingFilterValue,
MissingHeader(String),
NotFound(String),
OpenIndex(String),
FilterParsing(String),
RetrieveDocument(u64, String),
SearchDocuments(String),
UnknownFilteredAttribute,
}
impl ResponseError {
pub fn internal(err: impl fmt::Display) -> ResponseError {
ResponseError::Internal(err.to_string())
}
pub fn bad_request(err: impl fmt::Display) -> ResponseError {
ResponseError::BadRequest(err.to_string())
}
pub fn missing_authorization_header() -> ResponseError {
ResponseError::MissingAuthorizationHeader
}
pub fn invalid_token(err: impl fmt::Display) -> ResponseError {
ResponseError::InvalidToken(err.to_string())
}
pub fn not_found(err: impl fmt::Display) -> ResponseError {
ResponseError::NotFound(err.to_string())
}
pub fn index_not_found(err: impl fmt::Display) -> ResponseError {
ResponseError::IndexNotFound(err.to_string())
}
pub fn document_not_found(err: impl fmt::Display) -> ResponseError {
ResponseError::DocumentNotFound(err.to_string())
}
pub fn missing_header(err: impl fmt::Display) -> ResponseError {
ResponseError::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 open_index(err: impl fmt::Display) -> ResponseError {
ResponseError::OpenIndex(err.to_string())
}
pub fn create_index(err: impl fmt::Display) -> ResponseError {
ResponseError::CreateIndex(err.to_string())
}
pub fn invalid_index_uid() -> ResponseError {
ResponseError::InvalidIndexUid
}
pub fn maintenance() -> ResponseError {
ResponseError::Maintenance
}
pub fn retrieve_document(doc_id: u64, err: impl fmt::Display) -> ResponseError {
ResponseError::RetrieveDocument(doc_id, err.to_string())
}
pub fn search_documents(err: impl fmt::Display) -> ResponseError {
ResponseError::SearchDocuments(err.to_string())
}
}
impl fmt::Display for ResponseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Internal(err) => write!(f, "{}", err),
Self::BadRequest(err) => write!(f, "{}", err),
Self::MissingAuthorizationHeader => write!(f, "You must have an authorization token"),
Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err),
Self::NotFound(err) => write!(f, "{} not found", err),
Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid),
Self::DocumentNotFound(document_id) => write!(f, "Document with id {} not found", document_id),
Self::MissingHeader(header) => write!(f, "Header {} is missing", header),
Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err),
Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err),
Self::BadRequest(err) => write!(f, "{}", err),
Self::CreateIndex(err) => write!(f, "Impossible to create index; {}", err),
Self::InvalidIndexUid => write!(f, "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."),
Self::Maintenance => write!(f, "Server is in maintenance, please try again later"),
Self::DocumentNotFound(document_id) => write!(f, "Document with id {} not found", document_id),
Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid),
Self::Internal(err) => write!(f, "{}", err),
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::MissingFilterValue => f.write_str("a filter doesn't have a value to compare it with"),
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::UnknownFilteredAttribute => f.write_str("a filter is specifying an unknown schema attribute"),
}
}
}
@ -53,20 +123,66 @@ impl aweb::error::ResponseError for ResponseError {
fn status_code(&self) -> StatusCode {
match *self {
Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::BadRequest(_) => StatusCode::BAD_REQUEST,
Self::MissingAuthorizationHeader => StatusCode::FORBIDDEN,
Self::InvalidToken(_) => StatusCode::UNAUTHORIZED,
Self::NotFound(_) => StatusCode::NOT_FOUND,
Self::IndexNotFound(_) => StatusCode::NOT_FOUND,
Self::DocumentNotFound(_) => StatusCode::NOT_FOUND,
Self::MissingHeader(_) => StatusCode::UNAUTHORIZED,
Self::BadParameter(_, _) => StatusCode::BAD_REQUEST,
Self::OpenIndex(_) => StatusCode::BAD_REQUEST,
Self::BadRequest(_) => StatusCode::BAD_REQUEST,
Self::CreateIndex(_) => StatusCode::BAD_REQUEST,
Self::DocumentNotFound(_) => StatusCode::NOT_FOUND,
Self::IndexNotFound(_) => StatusCode::NOT_FOUND,
Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::InvalidIndexUid => StatusCode::BAD_REQUEST,
Self::InvalidToken(_) => StatusCode::UNAUTHORIZED,
Self::Maintenance => StatusCode::SERVICE_UNAVAILABLE,
Self::FilterParsing(_) => StatusCode::BAD_REQUEST,
Self::MissingAuthorizationHeader => StatusCode::FORBIDDEN,
Self::MissingFilterValue => StatusCode::BAD_REQUEST,
Self::MissingHeader(_) => StatusCode::UNAUTHORIZED,
Self::NotFound(_) => StatusCode::NOT_FOUND,
Self::OpenIndex(_) => StatusCode::BAD_REQUEST,
Self::RetrieveDocument(_, _) => StatusCode::BAD_REQUEST,
Self::SearchDocuments(_) => StatusCode::BAD_REQUEST,
Self::UnknownFilteredAttribute => StatusCode::BAD_REQUEST,
}
}
}
impl From<meilisearch_core::HeedError> for ResponseError {
fn from(err: meilisearch_core::HeedError) -> ResponseError {
ResponseError::Internal(err.to_string())
}
}
impl From<meilisearch_core::FstError> for ResponseError {
fn from(err: meilisearch_core::FstError) -> ResponseError {
ResponseError::Internal(err.to_string())
}
}
impl From<meilisearch_core::Error> for ResponseError {
fn from(err: meilisearch_core::Error) -> ResponseError {
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());
ResponseError::FilterParsing(message)
},
_ => ResponseError::Internal(err.to_string()),
}
}
}
impl From<meilisearch_schema::Error> for ResponseError {
fn from(err: meilisearch_schema::Error) -> ResponseError {
ResponseError::Internal(err.to_string())
}
}
impl From<actix_http::Error> for ResponseError {
fn from(err: actix_http::Error) -> ResponseError {
ResponseError::Internal(err.to_string())
}
}

View File

@ -1,8 +1,5 @@
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::convert::From;
use std::error;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::time::{Duration, Instant};
@ -19,74 +16,7 @@ use serde_json::Value;
use siphasher::sip::SipHasher;
use slice_group_by::GroupBy;
#[derive(Debug)]
pub enum Error {
SearchDocuments(String),
RetrieveDocument(u64, String),
DocumentNotFound(u64),
CropFieldWrongType(String),
FilterParsing(String),
AttributeNotFoundOnDocument(String),
AttributeNotFoundOnSchema(String),
MissingFilterValue,
UnknownFilteredAttribute,
Internal(String),
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
SearchDocuments(err) => write!(f, "impossible to search documents; {}", err),
RetrieveDocument(id, err) => write!(
f,
"impossible to retrieve the document with id: {}; {}",
id, err
),
DocumentNotFound(id) => write!(f, "document {} not found", id),
CropFieldWrongType(field) => {
write!(f, "the field {} cannot be cropped it's not a string", field)
}
AttributeNotFoundOnDocument(field) => {
write!(f, "field {} is not found on document", field)
}
AttributeNotFoundOnSchema(field) => write!(f, "field {} is not found on schema", field),
MissingFilterValue => f.write_str("a filter doesn't have a value to compare it with"),
UnknownFilteredAttribute => {
f.write_str("a filter is specifying an unknown schema attribute")
}
Internal(err) => write!(f, "internal error; {}", err),
FilterParsing(err) => write!(f, "filter parsing error: {}", err),
}
}
}
impl From<meilisearch_core::Error> for Error {
fn from(error: meilisearch_core::Error) -> Self {
use meilisearch_core::pest_error::LineColLocation::*;
match error {
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)
},
_ => Error::Internal(error.to_string()),
}
}
}
impl From<heed::Error> for Error {
fn from(error: heed::Error) -> Self {
Error::Internal(error.to_string())
}
}
use crate::error::ResponseError;
pub trait IndexSearchExt {
fn new_search(&self, query: String) -> SearchBuilder;
@ -169,17 +99,14 @@ impl<'a> SearchBuilder<'a> {
self
}
pub fn search(&self, reader: &heed::RoTxn<MainT>) -> Result<SearchResult, Error> {
let schema = self.index.main.schema(reader);
let schema = schema.map_err(|e| Error::Internal(e.to_string()))?;
let schema = match schema {
Some(schema) => schema,
None => return Err(Error::Internal(String::from("missing schema"))),
};
pub fn search(&self, reader: &heed::RoTxn<MainT>) -> Result<SearchResult, ResponseError> {
let schema = self
.index
.main
.schema(reader)?
.ok_or(ResponseError::internal("missing schema"))?;
let ranked_map = self.index.main.ranked_map(reader);
let ranked_map = ranked_map.map_err(|e| Error::Internal(e.to_string()))?;
let ranked_map = ranked_map.unwrap_or_default();
let ranked_map = self.index.main.ranked_map(reader)?.unwrap_or_default();
// Change criteria
let mut query_builder = match self.get_criteria(reader, &ranked_map, &schema)? {
@ -221,9 +148,8 @@ impl<'a> SearchBuilder<'a> {
}
let start = Instant::now();
let result =
query_builder.query(reader, &self.query, self.offset..(self.offset + self.limit));
let (docs, nb_hits) = result.map_err(|e| Error::SearchDocuments(e.to_string()))?;
let result = query_builder.query(reader, &self.query, self.offset..(self.offset + self.limit));
let (docs, nb_hits) = result.map_err(ResponseError::search_documents)?;
let time_ms = start.elapsed().as_millis() as usize;
let mut all_attributes: HashSet<&str> = HashSet::new();
@ -255,11 +181,19 @@ impl<'a> SearchBuilder<'a> {
let mut hits = Vec::with_capacity(self.limit);
for doc in docs {
// retrieve the content of document in kv store
let attributes: Option<HashSet<&str>> = self
.attributes_to_retrieve
.as_ref()
.map(|a| a.iter().map(|a| a.as_str()).collect());
let mut document: IndexMap<String, Value> = self
.index
.document(reader, Some(&all_attributes), doc.id)
.map_err(|e| Error::RetrieveDocument(doc.id.0, e.to_string()))?
.ok_or(Error::DocumentNotFound(doc.id.0))?;
.document(reader, attributes.as_ref(), doc.id)
.map_err(|e| ResponseError::retrieve_document(doc.id.0, e))?
.ok_or(ResponseError::internal(
"Impossible to retrieve a document id returned by the engine",
))?;
let mut formatted = document.iter()
.filter(|(key, _)| all_formatted.contains(key.as_str()))
@ -320,7 +254,7 @@ impl<'a> SearchBuilder<'a> {
reader: &heed::RoTxn<MainT>,
ranked_map: &'a RankedMap,
schema: &Schema,
) -> Result<Option<Criteria<'a>>, Error> {
) -> Result<Option<Criteria<'a>>, ResponseError> {
let ranking_rules = self.index.main.ranking_rules(reader)?;
if let Some(ranking_rules) = ranking_rules {

View File

@ -1,7 +1,5 @@
use std::collections::HashSet;
use std::collections::BTreeSet;
use std::collections::{BTreeSet, HashSet};
use actix_web as aweb;
use actix_web::{delete, get, post, put, web, HttpResponse};
use indexmap::IndexMap;
use serde::Deserialize;
@ -23,22 +21,18 @@ pub struct DocumentParam {
pub async fn get_document(
data: web::Data<Data>,
path: web::Path<DocumentParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let document_id = meilisearch_core::serde::compute_document_id(path.document_id.clone());
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let document_id = meilisearch_core::serde::compute_document_id(&path.document_id);
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let response = index
.document::<Document>(&reader, None, document_id)
.map_err(|_| ResponseError::DocumentNotFound(path.document_id.clone()))?
.ok_or(ResponseError::DocumentNotFound(path.document_id.clone()))?;
.document::<Document>(&reader, None, document_id)?
.ok_or(ResponseError::document_not_found(&path.document_id))?;
Ok(HttpResponse::Ok().json(response))
}
@ -47,28 +41,21 @@ pub async fn get_document(
pub async fn delete_document(
data: web::Data<Data>,
path: web::Path<DocumentParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let document_id = meilisearch_core::serde::compute_document_id(path.document_id.clone());
.ok_or(ResponseError::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()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut update_writer = data.db.update_write_txn()?;
let mut documents_deletion = index.documents_deletion();
documents_deletion.delete_document_by_id(document_id);
let update_id = documents_deletion
.finalize(&mut update_writer)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = documents_deletion.finalize(&mut update_writer)?;
update_writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
update_writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -86,32 +73,29 @@ pub async fn get_all_documents(
data: web::Data<Data>,
path: web::Path<IndexParam>,
params: web::Query<BrowseQuery>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let offset = params.offset.unwrap_or(0);
let limit = params.limit.unwrap_or(20);
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let documents_ids: Result<BTreeSet<_>, _> = index
.documents_fields_counts
.documents_ids(&reader)
.map_err(|_| ResponseError::Internal(path.index_uid.clone()))?
.documents_ids(&reader)?
.skip(offset)
.take(limit)
.collect();
let documents_ids = documents_ids.map_err(|err| ResponseError::Internal(err.to_string()))?;
let documents_ids = documents_ids?;
let attributes: Option<HashSet<&str>> = params
.attributes_to_retrieve.as_ref()
.attributes_to_retrieve
.as_ref()
.map(|a| a.split(',').collect());
let mut response = Vec::<Document>::new();
@ -145,48 +129,33 @@ async fn update_multiple_documents(
params: web::Query<UpdateDocumentsQuery>,
body: web::Json<Vec<Document>>,
is_partial: bool,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let mut schema = index
.main
.schema(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to retrieve the schema".to_string(),
))?;
.schema(&reader)?
.ok_or(ResponseError::internal("Impossible to retrieve the schema"))?;
if schema.primary_key().is_none() {
let id = match params.primary_key.clone() {
Some(id) => id,
None => body.first().and_then(|docs| find_primary_key(docs)).ok_or(
ResponseError::BadRequest("Could not infer a primary key".to_string()),
)?,
let id = match &params.primary_key {
Some(id) => id.to_string(),
None => body
.first()
.and_then(find_primary_key)
.ok_or(ResponseError::bad_request("Could not infer a primary key"))?,
};
let mut writer = data
.db
.main_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.main_write_txn()?;
schema
.set_primary_key(&id)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
index
.main
.put_schema(&mut writer, &schema)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
schema.set_primary_key(&id)?;
index.main.put_schema(&mut writer, &schema)?;
writer.commit()?;
}
let mut document_addition = if is_partial {
@ -199,16 +168,9 @@ async fn update_multiple_documents(
document_addition.update_document(document);
}
let mut update_writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = document_addition
.finalize(&mut update_writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
update_writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut update_writer = data.db.update_write_txn()?;
let update_id = document_addition.finalize(&mut update_writer)?;
update_writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -219,7 +181,7 @@ pub async fn add_documents(
path: web::Path<IndexParam>,
params: web::Query<UpdateDocumentsQuery>,
body: web::Json<Vec<Document>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
update_multiple_documents(data, path, params, body, false).await
}
@ -229,7 +191,7 @@ pub async fn update_documents(
path: web::Path<IndexParam>,
params: web::Query<UpdateDocumentsQuery>,
body: web::Json<Vec<Document>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
update_multiple_documents(data, path, params, body, true).await
}
@ -238,16 +200,13 @@ pub async fn delete_documents(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Vec<Value>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let mut documents_deletion = index.documents_deletion();
@ -258,13 +217,9 @@ pub async fn delete_documents(
}
}
let update_id = documents_deletion
.finalize(&mut writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
let update_id = documents_deletion.finalize(&mut writer)?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -273,24 +228,17 @@ pub async fn delete_documents(
pub async fn clear_all_documents(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let update_id = index
.clear_all(&mut writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
let update_id = index.clear_all(&mut writer)?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}

View File

@ -1,6 +1,5 @@
use crate::error::ResponseError;
use crate::Data;
use actix_web as aweb;
use actix_web::{get, put, web, HttpResponse};
use heed::types::{Str, Unit};
use serde::Deserialize;
@ -8,49 +7,32 @@ use serde::Deserialize;
const UNHEALTHY_KEY: &str = "_is_unhealthy";
#[get("/health")]
pub async fn get_health(data: web::Data<Data>) -> aweb::Result<HttpResponse> {
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
pub async fn get_health(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
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.into());
return Err(ResponseError::Maintenance);
}
Ok(HttpResponse::Ok().finish())
}
pub async fn set_healthy(data: web::Data<Data>) -> aweb::Result<HttpResponse> {
let mut writer = data
.db
.main_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
pub async fn set_healthy(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
let mut writer = data.db.main_write_txn()?;
let common_store = data.db.common_store();
common_store
.delete::<_, Str>(&mut writer, UNHEALTHY_KEY)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY)?;
writer.commit()?;
Ok(HttpResponse::Ok().finish())
}
pub async fn set_unhealthy(data: web::Data<Data>) -> aweb::Result<HttpResponse> {
let mut writer = data
.db
.main_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
pub async fn set_unhealthy(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
let mut writer = data.db.main_write_txn()?;
let common_store = data.db.common_store();
common_store
.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &())
.map_err(|e| ResponseError::Internal(e.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &())?;
writer.commit()?;
Ok(HttpResponse::Ok().finish())
}
@ -64,7 +46,7 @@ pub struct HealtBody {
pub async fn change_healthyness(
data: web::Data<Data>,
body: web::Json<HealtBody>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
if body.health {
set_healthy(data).await
} else {

View File

@ -1,4 +1,3 @@
use actix_web as aweb;
use actix_web::{delete, get, post, put, web, HttpResponse};
use chrono::{DateTime, Utc};
use log::error;
@ -29,11 +28,8 @@ pub struct IndexResponse {
}
#[get("/indexes")]
pub async fn list_indexes(data: web::Data<Data>) -> aweb::Result<HttpResponse> {
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
pub async fn list_indexes(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
let reader = data.db.main_read_txn()?;
let mut response = Vec::new();
@ -42,26 +38,20 @@ pub async fn list_indexes(data: web::Data<Data>) -> aweb::Result<HttpResponse> {
match index {
Some(index) => {
let name = index
.main
.name(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the name of an index".to_string(),
let name = index.main.name(&reader)?.ok_or(ResponseError::internal(
"Impossible to get the name of an index",
))?;
let created_at = index
.main
.created_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the create date of an index".to_string(),
.created_at(&reader)?
.ok_or(ResponseError::internal(
"Impossible to get the create date of an index",
))?;
let updated_at = index
.main
.updated_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the last update date of an index".to_string(),
.updated_at(&reader)?
.ok_or(ResponseError::internal(
"Impossible to get the last update date of an index",
))?;
let primary_key = match index.main.schema(&reader) {
@ -95,37 +85,28 @@ pub async fn list_indexes(data: web::Data<Data>) -> aweb::Result<HttpResponse> {
pub async fn get_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let name = index
.main
.name(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the name of an index".to_string(),
let name = index.main.name(&reader)?.ok_or(ResponseError::internal(
"Impossible to get the name of an index",
))?;
let created_at = index
.main
.created_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the create date of an index".to_string(),
.created_at(&reader)?
.ok_or(ResponseError::internal(
"Impossible to get the create date of an index",
))?;
let updated_at = index
.main
.updated_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the last update date of an index".to_string(),
.updated_at(&reader)?
.ok_or(ResponseError::internal(
"Impossible to get the last update date of an index",
))?;
let primary_key = match index.main.schema(&reader) {
@ -157,11 +138,11 @@ pub struct IndexCreateRequest {
pub async fn create_index(
data: web::Data<Data>,
body: web::Json<IndexCreateRequest>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
if let (None, None) = (body.name.clone(), body.uid.clone()) {
return Err(
ResponseError::BadRequest("Index creation must have an uid".to_string()).into(),
);
return Err(ResponseError::bad_request(
"Index creation must have an uid",
));
}
let uid = match body.uid.clone() {
@ -172,7 +153,7 @@ pub async fn create_index(
{
uid
} else {
return Err(ResponseError::InvalidIndexUid.into());
return Err(ResponseError::InvalidIndexUid);
}
}
None => loop {
@ -186,50 +167,33 @@ pub async fn create_index(
let created_index = data
.db
.create_index(&uid)
.map_err(|e| ResponseError::CreateIndex(e.to_string()))?;
.map_err(ResponseError::create_index)?;
let mut writer = data
.db
.main_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.main_write_txn()?;
let name = body.name.clone().unwrap_or(uid.clone());
created_index
.main
.put_name(&mut writer, &name)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
created_index.main.put_name(&mut writer, &name)?;
let created_at = created_index
.main
.created_at(&writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal("".to_string()))?;
.created_at(&writer)?
.ok_or(ResponseError::internal("Impossible to read created at"))?;
let updated_at = created_index
.main
.updated_at(&writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal("".to_string()))?;
.updated_at(&writer)?
.ok_or(ResponseError::internal("Impossible to read updated at"))?;
if let Some(id) = body.primary_key.clone() {
if let Some(mut schema) = created_index
.main
.schema(&writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?
{
if let Some(mut schema) = created_index.main.schema(&writer)? {
schema
.set_primary_key(&id)
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
created_index
.main
.put_schema(&mut writer, &schema)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
.map_err(ResponseError::bad_request)?;
created_index.main.put_schema(&mut writer, &schema)?;
}
}
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer.commit()?;
Ok(HttpResponse::Created().json(IndexResponse {
name,
@ -262,83 +226,53 @@ pub async fn update_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<IndexCreateRequest>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let mut writer = data
.db
.main_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.main_write_txn()?;
if let Some(name) = body.name.clone() {
index
.main
.put_name(&mut writer, &name)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
index.main.put_name(&mut writer, &name)?;
}
if let Some(id) = body.primary_key.clone() {
if let Some(mut schema) = index
.main
.schema(&writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?
{
if let Some(mut schema) = index.main.schema(&writer)? {
match schema.primary_key() {
Some(_) => {
return Err(ResponseError::BadRequest(
"The primary key cannot be updated".to_string(),
)
.into());
return Err(ResponseError::bad_request(
"The primary key cannot be updated",
));
}
None => {
schema
.set_primary_key(&id)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
index
.main
.put_schema(&mut writer, &schema)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
schema.set_primary_key(&id)?;
index.main.put_schema(&mut writer, &schema)?;
}
}
}
}
index
.main
.put_updated_at(&mut writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
index.main.put_updated_at(&mut writer)?;
writer.commit()?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let name = index
.main
.name(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the name of an index".to_string(),
let name = index.main.name(&reader)?.ok_or(ResponseError::internal(
"Impossible to get the name of an index",
))?;
let created_at = index
.main
.created_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the create date of an index".to_string(),
.created_at(&reader)?
.ok_or(ResponseError::internal(
"Impossible to get the create date of an index",
))?;
let updated_at = index
.main
.updated_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to get the last update date of an index".to_string(),
.updated_at(&reader)?
.ok_or(ResponseError::internal(
"Impossible to get the last update date of an index",
))?;
let primary_key = match index.main.schema(&reader) {
@ -362,12 +296,10 @@ pub async fn update_index(
pub async fn delete_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
data.db
.delete_index(&path.index_uid)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
) -> Result<HttpResponse, ResponseError> {
data.db.delete_index(&path.index_uid)?;
HttpResponse::NoContent().await
Ok(HttpResponse::NoContent().finish())
}
#[derive(Default, Deserialize)]
@ -380,24 +312,22 @@ pub struct UpdateParam {
pub async fn get_update_status(
data: web::Data<Data>,
path: web::Path<UpdateParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data
.db
.update_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.update_read_txn()?;
let status = index
.update_status(&reader, path.update_id)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
let status = index.update_status(&reader, path.update_id)?;
match status {
Some(status) => Ok(HttpResponse::Ok().json(status)),
None => Err(ResponseError::NotFound(format!("Update {} not found", path.update_id)).into()),
None => Err(ResponseError::NotFound(format!(
"Update {} not found",
path.update_id
))),
}
}
@ -405,20 +335,15 @@ pub async fn get_update_status(
pub async fn get_all_updates_status(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data
.db
.update_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.update_read_txn()?;
let response = index
.all_updates_status(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let response = index.all_updates_status(&reader)?;
Ok(HttpResponse::Ok().json(response))
}

View File

@ -2,13 +2,11 @@ use std::collections::{HashSet, HashMap};
use std::time::Duration;
use log::warn;
use meilisearch_core::Index;
use actix_web as aweb;
use actix_web::{get, web};
use serde::{Deserialize};
use serde::Deserialize;
use crate::error::ResponseError;
use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchResult};
use crate::helpers::meilisearch::{IndexSearchExt, SearchResult};
use crate::routes::IndexParam;
use crate::Data;
@ -32,24 +30,18 @@ pub async fn search_with_url_query(
data: web::Data<Data>,
path: web::Path<IndexParam>,
params: web::Query<SearchQuery>,
) -> aweb::Result<web::Json<SearchResult>> {
) -> Result<web::Json<SearchResult>, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let schema = index
.main
.schema(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.ok_or(ResponseError::Internal(
"Impossible to retrieve the schema".to_string(),
))?;
.schema(&reader)?
.ok_or(ResponseError::internal("Impossible to retrieve the schema"))?;
let mut search_builder = index.new_search(params.q.clone());
@ -145,11 +137,5 @@ pub async fn search_with_url_query(
}
}
let response = match search_builder.search(&reader) {
Ok(response) => response,
Err(Error::Internal(message)) => return Err(ResponseError::Internal(message).into()),
Err(others) => return Err(ResponseError::BadRequest(others.to_string()).into()),
};
Ok(web::Json(response))
Ok(web::Json(search_builder.search(&reader)?))
}

View File

@ -1,4 +1,3 @@
use actix_web as aweb;
use actix_web::{delete, get, post, web, HttpResponse};
use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES};
use std::collections::{BTreeMap, BTreeSet, HashSet};
@ -12,26 +11,19 @@ pub async fn update_all(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Settings>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let settings = body
.into_inner()
.into_update()
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.map_err(ResponseError::bad_request)?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -40,71 +32,42 @@ pub async fn update_all(
pub async fn get_all(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let stop_words_fst = index
.main
.stop_words_fst(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let stop_words = stop_words_fst
.unwrap_or_default()
.stream()
.into_strs()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let stop_words_fst = index.main.stop_words_fst(&reader)?;
let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs()?;
let stop_words: BTreeSet<String> = stop_words.into_iter().collect();
let synonyms_fst = index
.main
.synonyms_fst(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.unwrap_or_default();
let synonyms_list = synonyms_fst
.stream()
.into_strs()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let synonyms_fst = index.main.synonyms_fst(&reader)?.unwrap_or_default();
let synonyms_list = synonyms_fst.stream().into_strs()?;
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())
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?;
if let Some(list) = alternative_list {
let list = list
.stream()
.into_strs()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let list = list.stream().into_strs()?;
synonyms.insert(synonym, list);
}
}
let ranking_rules = index
.main
.ranking_rules(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.ranking_rules(&reader)?
.unwrap_or(DEFAULT_RANKING_RULES.to_vec())
.into_iter()
.map(|r| r.to_string())
.collect();
let distinct_attribute = index
.main
.distinct_attribute(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let distinct_attribute = index.main.distinct_attribute(&reader)?;
let schema = index
.main
.schema(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let schema = index.main.schema(&reader)?;
let searchable_attributes = schema.clone().map(|s| {
s.indexed_name()
@ -139,15 +102,12 @@ pub async fn get_all(
pub async fn delete_all(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let mut writer = data.db.update_write_txn()?;
let settings = SettingsUpdate {
ranking_rules: UpdateState::Clear,
@ -160,12 +120,8 @@ pub async fn delete_all(
accept_new_fields: UpdateState::Clear,
};
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -174,20 +130,16 @@ pub async fn delete_all(
pub async fn get_rules(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data.db.main_read_txn()?;
let ranking_rules = index
.main
.ranking_rules(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.ranking_rules(&reader)?
.unwrap_or(DEFAULT_RANKING_RULES.to_vec())
.into_iter()
.map(|r| r.to_string())
@ -201,30 +153,21 @@ pub async fn update_rules(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<Vec<String>>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = Settings {
ranking_rules: Some(body.into_inner()),
..Settings::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let settings = settings
.into_update()
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -233,28 +176,21 @@ pub async fn update_rules(
pub async fn delete_rules(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::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)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index.settings_update(&mut writer, settings)?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -263,19 +199,13 @@ pub async fn delete_rules(
pub async fn get_distinct(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let distinct_attribute = index
.main
.distinct_attribute(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data.db.main_read_txn()?;
let distinct_attribute = index.main.distinct_attribute(&reader)?;
Ok(HttpResponse::Ok().json(distinct_attribute))
}
@ -285,30 +215,21 @@ pub async fn update_distinct(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<String>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = Settings {
distinct_attribute: Some(body.into_inner()),
..Settings::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let settings = settings
.into_update()
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -317,28 +238,21 @@ pub async fn update_distinct(
pub async fn delete_distinct(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::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)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index.settings_update(&mut writer, settings)?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -347,19 +261,13 @@ pub async fn delete_distinct(
pub async fn get_searchable(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let schema = index
.main
.schema(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data.db.main_read_txn()?;
let schema = index.main.schema(&reader)?;
let searchable_attributes: Option<Vec<String>> =
schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect());
@ -371,30 +279,21 @@ pub async fn update_searchable(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<Vec<String>>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = Settings {
searchable_attributes: Some(body.into_inner()),
..Settings::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let settings = settings
.into_update()
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -403,27 +302,20 @@ pub async fn update_searchable(
pub async fn delete_searchable(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = SettingsUpdate {
searchable_attributes: UpdateState::Clear,
..SettingsUpdate::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -432,20 +324,14 @@ pub async fn delete_searchable(
pub async fn get_displayed(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data.db.main_read_txn()?;
let schema = index
.main
.schema(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let schema = index.main.schema(&reader)?;
let displayed_attributes: Option<HashSet<String>> = schema.map(|s| {
s.displayed_name()
@ -462,30 +348,21 @@ pub async fn update_displayed(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<HashSet<String>>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = Settings {
displayed_attributes: Some(body.into_inner()),
..Settings::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let settings = settings
.into_update()
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -494,27 +371,20 @@ pub async fn update_displayed(
pub async fn delete_displayed(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = SettingsUpdate {
displayed_attributes: UpdateState::Clear,
..SettingsUpdate::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -523,20 +393,14 @@ pub async fn delete_displayed(
pub async fn get_accept_new_fields(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data.db.main_read_txn()?;
let schema = index
.main
.schema(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let schema = index.main.schema(&reader)?;
let accept_new_fields = schema.map(|s| s.accept_new_fields());
@ -548,30 +412,21 @@ pub async fn update_accept_new_fields(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<bool>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = Settings {
accept_new_fields: Some(body.into_inner()),
..Settings::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let settings = settings
.into_update()
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}

View File

@ -1,6 +1,5 @@
use std::collections::HashMap;
use actix_web as aweb;
use actix_web::{get, web};
use chrono::{DateTime, Utc};
use log::error;
@ -25,36 +24,22 @@ pub struct IndexStatsResponse {
pub async fn index_stats(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<web::Json<IndexStatsResponse>> {
) -> Result<web::Json<IndexStatsResponse>, ResponseError> {
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.open_index(&path.index_uid)
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let number_of_documents = index
.main
.number_of_documents(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let number_of_documents = index.main.number_of_documents(&reader)?;
let fields_frequency = index
.main
.fields_frequency(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.unwrap_or_default();
let fields_frequency = index.main.fields_frequency(&reader)?.unwrap_or_default();
let update_reader = data
.db
.update_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_reader = data.db.update_read_txn()?;
let is_indexing = data
.is_indexing(&update_reader, &path.index_uid)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.is_indexing(&update_reader, &path.index_uid)?
.unwrap_or_default();
Ok(web::Json(IndexStatsResponse {
@ -73,37 +58,23 @@ pub struct StatsResult {
}
#[get("/stats")]
pub async fn get_stats(data: web::Data<Data>) -> aweb::Result<web::Json<StatsResult>> {
pub async fn get_stats(data: web::Data<Data>) -> Result<web::Json<StatsResult>, ResponseError> {
let mut index_list = HashMap::new();
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_reader = data
.db
.update_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let update_reader = data.db.update_read_txn()?;
let indexes_set = data.db.indexes_uids();
for index_uid in indexes_set {
let index = data.db.open_index(&index_uid);
match index {
Some(index) => {
let number_of_documents = index
.main
.number_of_documents(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let number_of_documents = index.main.number_of_documents(&reader)?;
let fields_frequency = index
.main
.fields_frequency(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.unwrap_or_default();
let fields_frequency = index.main.fields_frequency(&reader)?.unwrap_or_default();
let is_indexing = data
.is_indexing(&update_reader, &index_uid)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.is_indexing(&update_reader, &index_uid)?
.unwrap_or_default();
let response = IndexStatsResponse {
@ -120,16 +91,14 @@ pub async fn get_stats(data: web::Data<Data>) -> aweb::Result<web::Json<StatsRes
}
}
let database_size = WalkDir::new(data.db_path.clone())
let database_size = WalkDir::new(&data.db_path)
.into_iter()
.filter_map(|entry| entry.ok())
.filter_map(|entry| entry.metadata().ok())
.filter(|metadata| metadata.is_file())
.fold(0, |acc, m| acc + m.len());
let last_update = data
.last_update(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let last_update = data.last_update(&reader)?;
Ok(web::Json(StatsResult {
database_size,

View File

@ -1,32 +1,23 @@
use std::collections::BTreeSet;
use actix_web as aweb;
use actix_web::{delete, get, post, web, HttpResponse};
use meilisearch_core::settings::{SettingsUpdate, UpdateState};
use std::collections::BTreeSet;
use crate::error::ResponseError;
use crate::routes::{IndexParam, IndexUpdateResponse};
use crate::Data;
#[get("/indexes/{index_uid}/settings/stop-words")]
pub async fn get(data: web::Data<Data>, path: web::Path<IndexParam>) -> aweb::Result<HttpResponse> {
pub async fn get(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let stop_words_fst = index
.main
.stop_words_fst(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let stop_words = stop_words_fst
.unwrap_or_default()
.stream()
.into_strs()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
.ok_or(ResponseError::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.unwrap_or_default().stream().into_strs()?;
Ok(HttpResponse::Ok().json(stop_words))
}
@ -36,27 +27,20 @@ pub async fn update(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<BTreeSet<String>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = SettingsUpdate {
stop_words: UpdateState::Update(body.into_inner()),
..SettingsUpdate::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -65,27 +49,20 @@ pub async fn update(
pub async fn delete(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = SettingsUpdate {
stop_words: UpdateState::Clear,
..SettingsUpdate::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}

View File

@ -1,6 +1,5 @@
use std::collections::BTreeMap;
use actix_web as aweb;
use actix_web::{delete, get, post, web, HttpResponse};
use indexmap::IndexMap;
use meilisearch_core::settings::{SettingsUpdate, UpdateState};
@ -10,41 +9,27 @@ use crate::routes::{IndexParam, IndexUpdateResponse};
use crate::Data;
#[get("/indexes/{index_uid}/settings/synonyms")]
pub async fn get(data: web::Data<Data>, path: web::Path<IndexParam>) -> aweb::Result<HttpResponse> {
pub async fn get(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let reader = data
.db
.main_read_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let reader = data.db.main_read_txn()?;
let synonyms_fst = index
.main
.synonyms_fst(&reader)
.map_err(|err| ResponseError::Internal(err.to_string()))?
.unwrap_or_default();
let synonyms_list = synonyms_fst
.stream()
.into_strs()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let synonyms_fst = index.main.synonyms_fst(&reader)?.unwrap_or_default();
let synonyms_list = synonyms_fst.stream().into_strs()?;
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())
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?;
if let Some(list) = alternative_list {
let list = list
.stream()
.into_strs()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let list = list.stream().into_strs()?;
synonyms.insert(synonym, list);
}
}
@ -57,27 +42,20 @@ pub async fn update(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<BTreeMap<String, Vec<String>>>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = SettingsUpdate {
synonyms: UpdateState::Update(body.into_inner()),
..SettingsUpdate::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let update_id = index.settings_update(&mut writer, settings)?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}
@ -86,28 +64,21 @@ pub async fn update(
pub async fn delete(
data: web::Data<Data>,
path: web::Path<IndexParam>,
) -> aweb::Result<HttpResponse> {
) -> Result<HttpResponse, ResponseError> {
let index = data
.db
.open_index(&path.index_uid)
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
.ok_or(ResponseError::index_not_found(&path.index_uid))?;
let settings = SettingsUpdate {
synonyms: UpdateState::Clear,
..SettingsUpdate::default()
};
let mut writer = data
.db
.update_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let update_id = index
.settings_update(&mut writer, settings)
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut writer = data.db.update_write_txn()?;
let update_id = index.settings_update(&mut writer, settings)?;
writer
.commit()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
writer.commit()?;
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
}