adds error handling and integration

This commit is contained in:
mposmta 2020-04-06 20:05:02 +02:00 committed by marin
parent 83f50914ec
commit 3e031d8297
4 changed files with 57 additions and 44 deletions

View File

@ -1,10 +1,13 @@
use crate::serde::{DeserializerError, SerializerError}; use crate::serde::{DeserializerError, SerializerError};
use serde_json::Error as SerdeJsonError; use serde_json::Error as SerdeJsonError;
use pest::error::Error as PestError;
use crate::filters::Rule;
use std::{error, fmt, io}; use std::{error, fmt, io};
pub use heed::Error as HeedError;
pub use fst::Error as FstError;
pub use bincode::Error as BincodeError; pub use bincode::Error as BincodeError;
pub use fst::Error as FstError;
pub use heed::Error as HeedError;
pub use pest::error as pest_error;
pub type MResult<T> = Result<T, Error>; pub type MResult<T> = Result<T, Error>;
@ -25,6 +28,7 @@ pub enum Error {
Serializer(SerializerError), Serializer(SerializerError),
Deserializer(DeserializerError), Deserializer(DeserializerError),
UnsupportedOperation(UnsupportedOperation), UnsupportedOperation(UnsupportedOperation),
FilterParseError(PestError<Rule>)
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
@ -42,11 +46,11 @@ impl From<PestError<Rule>> for Error {
Rule::not => "NOT", Rule::not => "NOT",
Rule::string => "string", Rule::string => "string",
Rule::word => "word", Rule::word => "word",
Rule::greater => "field>value", Rule::greater => "field > value",
Rule::less => "field<value", Rule::less => "field < value",
Rule::eq => "field:value", Rule::eq => "field = value",
Rule::leq => "field<=value", Rule::leq => "field <= value",
Rule::geq => "field>=value", Rule::geq => "field >= value",
Rule::key => "key", Rule::key => "key",
_ => "other", _ => "other",
}; };
@ -122,6 +126,7 @@ impl fmt::Display for Error {
Serializer(e) => write!(f, "serializer error; {}", e), Serializer(e) => write!(f, "serializer error; {}", e),
Deserializer(e) => write!(f, "deserializer error; {}", e), Deserializer(e) => write!(f, "deserializer error; {}", e),
UnsupportedOperation(op) => write!(f, "unsupported operation; {}", op), UnsupportedOperation(op) => write!(f, "unsupported operation; {}", op),
FilterParseError(e) => write!(f, "error parsing filter; {}", e),
} }
} }
} }

View File

@ -1,12 +1,15 @@
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
extern crate assert_matches; extern crate assert_matches;
#[macro_use]
extern crate pest_derive;
mod automaton; mod automaton;
mod bucket_sort; mod bucket_sort;
mod database; mod database;
mod distinct_map; mod distinct_map;
mod error; mod error;
mod filters;
mod levenshtein; mod levenshtein;
mod number; mod number;
mod query_builder; mod query_builder;
@ -23,7 +26,8 @@ pub mod serde;
pub mod store; pub mod store;
pub use self::database::{BoxUpdateFn, Database, MainT, UpdateT}; pub use self::database::{BoxUpdateFn, Database, MainT, UpdateT};
pub use self::error::{Error, HeedError, FstError, MResult}; pub use self::error::{Error, HeedError, FstError, MResult, pest_error};
pub use self::filters::Filter;
pub use self::number::{Number, ParseNumberError}; pub use self::number::{Number, ParseNumberError};
pub use self::ranked_map::RankedMap; pub use self::ranked_map::RankedMap;
pub use self::raw_document::RawDocument; pub use self::raw_document::RawDocument;

View File

@ -19,6 +19,7 @@ pub enum ResponseError {
IndexNotFound(String), IndexNotFound(String),
DocumentNotFound(String), DocumentNotFound(String),
MissingHeader(String), MissingHeader(String),
FilterParsing(String),
BadParameter(String, String), BadParameter(String, String),
OpenIndex(String), OpenIndex(String),
CreateIndex(String), CreateIndex(String),
@ -73,11 +74,15 @@ impl IntoResponse for ResponseError {
match self { match self {
ResponseError::Internal(err) => { ResponseError::Internal(err) => {
error!("internal server error: {}", err); error!("internal server error: {}", err);
error( error("Internal server error".to_string(),
String::from("Internal server error"),
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
) )
} }
ResponseError::FilterParsing(err) => {
warn!("error paring filter: {}", err);
error(format!("parsing error: {}", err),
StatusCode::BAD_REQUEST)
}
ResponseError::BadRequest(err) => { ResponseError::BadRequest(err) => {
warn!("bad request: {}", err); warn!("bad request: {}", err);
error(err, StatusCode::BAD_REQUEST) error(err, StatusCode::BAD_REQUEST)
@ -159,7 +164,10 @@ impl From<FstError> for ResponseError {
impl From<SearchError> for ResponseError { impl From<SearchError> for ResponseError {
fn from(err: SearchError) -> ResponseError { fn from(err: SearchError) -> ResponseError {
ResponseError::internal(err) match err {
SearchError::FilterParsing(s) => ResponseError::FilterParsing(s),
_ => ResponseError::internal(err),
}
} }
} }

View File

@ -8,6 +8,7 @@ use std::time::{Duration, Instant};
use indexmap::IndexMap; use indexmap::IndexMap;
use log::error; use log::error;
use meilisearch_core::Filter;
use meilisearch_core::criterion::*; use meilisearch_core::criterion::*;
use meilisearch_core::settings::RankingRule; use meilisearch_core::settings::RankingRule;
use meilisearch_core::{Highlight, Index, MainT, RankedMap}; use meilisearch_core::{Highlight, Index, MainT, RankedMap};
@ -23,6 +24,7 @@ pub enum Error {
RetrieveDocument(u64, String), RetrieveDocument(u64, String),
DocumentNotFound(u64), DocumentNotFound(u64),
CropFieldWrongType(String), CropFieldWrongType(String),
FilterParsing(String),
AttributeNotFoundOnDocument(String), AttributeNotFoundOnDocument(String),
AttributeNotFoundOnSchema(String), AttributeNotFoundOnSchema(String),
MissingFilterValue, MissingFilterValue,
@ -56,13 +58,26 @@ impl fmt::Display for Error {
f.write_str("a filter is specifying an unknown schema attribute") f.write_str("a filter is specifying an unknown schema attribute")
} }
Internal(err) => write!(f, "internal error; {}", err), Internal(err) => write!(f, "internal error; {}", err),
FilterParsing(err) => write!(f, "filter parsing error: {}", err),
} }
} }
} }
impl From<meilisearch_core::Error> for Error { impl From<meilisearch_core::Error> for Error {
fn from(error: meilisearch_core::Error) -> Self { fn from(error: meilisearch_core::Error) -> Self {
Error::Internal(error.to_string()) 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()),
}
} }
} }
@ -171,39 +186,20 @@ impl<'a> SearchBuilder<'a> {
None => self.index.query_builder(), None => self.index.query_builder(),
}; };
if let Some(filters) = &self.filters { if let Some(filter_expression) = &self.filters {
let mut split = filters.split(':'); let filter = Filter::parse(filter_expression, &schema)?;
match (split.next(), split.next()) { query_builder.with_filter(move |id| {
(Some(_), None) | (Some(_), Some("")) => return Err(Error::MissingFilterValue), let index = &self.index;
(Some(attr), Some(value)) => { let reader = &reader;
let ref_reader = reader; let filter = &filter;
let ref_index = &self.index; match filter.test(reader, index, id) {
let value = value.trim().to_lowercase(); Ok(res) => res,
Err(e) => {
let attr = match schema.id(attr) { log::warn!("unexpected error during filtering: {}", e);
Some(attr) => attr, false
None => return Err(Error::UnknownFilteredAttribute), }
};
query_builder.with_filter(move |id| {
let attr = attr;
let index = ref_index;
let reader = ref_reader;
match index.document_attribute::<Value>(reader, id, attr) {
Ok(Some(Value::String(s))) => s.to_lowercase() == value,
Ok(Some(Value::Bool(b))) => {
(value == "true" && b) || (value == "false" && !b)
}
Ok(Some(Value::Array(a))) => {
a.into_iter().any(|s| s.as_str() == Some(&value))
}
_ => false,
}
});
} }
(_, _) => (), });
}
} }
query_builder.with_fetch_timeout(self.timeout); query_builder.with_fetch_timeout(self.timeout);