mirror of
https://github.com/meilisearch/meilisearch.git
synced 2024-11-27 04:25:06 +08:00
commit
5ee4a1e954
@ -1,6 +1,6 @@
|
|||||||
## v0.10.2
|
## 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)
|
- Bump sentry version to 0.18.1 (#690)
|
||||||
- Enable max payload size override (#684)
|
- Enable max payload size override (#684)
|
||||||
- Disable sentry in debug (#681)
|
- Disable sentry in debug (#681)
|
||||||
@ -18,6 +18,7 @@
|
|||||||
- Update sentry features to remove openssl (#702)
|
- Update sentry features to remove openssl (#702)
|
||||||
- Add SSL support (#669)
|
- Add SSL support (#669)
|
||||||
- Rename fieldsFrequency into fieldsDistribution in stats (#719)
|
- Rename fieldsFrequency into fieldsDistribution in stats (#719)
|
||||||
|
- Add support for error code reporting (#703)
|
||||||
|
|
||||||
## v0.10.1
|
## v0.10.1
|
||||||
|
|
||||||
|
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -1653,6 +1653,7 @@ dependencies = [
|
|||||||
"jemallocator",
|
"jemallocator",
|
||||||
"levenshtein_automata",
|
"levenshtein_automata",
|
||||||
"log",
|
"log",
|
||||||
|
"meilisearch-error",
|
||||||
"meilisearch-schema",
|
"meilisearch-schema",
|
||||||
"meilisearch-tokenizer",
|
"meilisearch-tokenizer",
|
||||||
"meilisearch-types",
|
"meilisearch-types",
|
||||||
@ -1673,6 +1674,13 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "meilisearch-error"
|
||||||
|
version = "0.10.1"
|
||||||
|
dependencies = [
|
||||||
|
"actix-http",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilisearch-http"
|
name = "meilisearch-http"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
@ -1690,7 +1698,6 @@ dependencies = [
|
|||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
"heed",
|
|
||||||
"http 0.1.21",
|
"http 0.1.21",
|
||||||
"http-service",
|
"http-service",
|
||||||
"http-service-mock",
|
"http-service-mock",
|
||||||
@ -1699,6 +1706,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"main_error",
|
"main_error",
|
||||||
"meilisearch-core",
|
"meilisearch-core",
|
||||||
|
"meilisearch-error",
|
||||||
"meilisearch-schema",
|
"meilisearch-schema",
|
||||||
"meilisearch-tokenizer",
|
"meilisearch-tokenizer",
|
||||||
"mime",
|
"mime",
|
||||||
@ -1729,6 +1737,7 @@ version = "0.10.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"meilisearch-error",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"toml",
|
"toml",
|
||||||
|
@ -24,6 +24,7 @@ intervaltree = "0.2.5"
|
|||||||
itertools = "0.9.0"
|
itertools = "0.9.0"
|
||||||
levenshtein_automata = { version = "0.2.0", features = ["fst_automaton"] }
|
levenshtein_automata = { version = "0.2.0", features = ["fst_automaton"] }
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
|
meilisearch-error = { path = "../meilisearch-error", version = "0.10.1" }
|
||||||
meilisearch-schema = { path = "../meilisearch-schema", version = "0.10.1" }
|
meilisearch-schema = { path = "../meilisearch-schema", version = "0.10.1" }
|
||||||
meilisearch-tokenizer = { path = "../meilisearch-tokenizer", version = "0.10.1" }
|
meilisearch-tokenizer = { path = "../meilisearch-tokenizer", version = "0.10.1" }
|
||||||
meilisearch-types = { path = "../meilisearch-types", version = "0.10.1" }
|
meilisearch-types = { path = "../meilisearch-types", version = "0.10.1" }
|
||||||
|
@ -126,9 +126,7 @@ fn index_command(command: IndexCommand, database: Database) -> Result<(), Box<dy
|
|||||||
settings.into_update().unwrap()
|
settings.into_update().unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut update_writer = db.update_write_txn().unwrap();
|
db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
index.settings_update(&mut update_writer, settings)?;
|
|
||||||
update_writer.commit().unwrap();
|
|
||||||
|
|
||||||
let mut rdr = if command.csv_data_path.as_os_str() == "-" {
|
let mut rdr = if command.csv_data_path.as_os_str() == "-" {
|
||||||
csv::Reader::from_reader(Box::new(io::stdin()) as Box<dyn Read>)
|
csv::Reader::from_reader(Box::new(io::stdin()) as Box<dyn Read>)
|
||||||
@ -175,10 +173,9 @@ fn index_command(command: IndexCommand, database: Database) -> Result<(), Box<dy
|
|||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
let mut update_writer = db.update_write_txn().unwrap();
|
let update_id = db.update_write(|w| additions.finalize(w))?;
|
||||||
|
|
||||||
println!("committing update...");
|
println!("committing update...");
|
||||||
let update_id = additions.finalize(&mut update_writer)?;
|
|
||||||
update_writer.commit().unwrap();
|
|
||||||
max_update_id = max_update_id.max(update_id);
|
max_update_id = max_update_id.max(update_id);
|
||||||
println!("committed update {}", update_id);
|
println!("committed update {}", update_id);
|
||||||
}
|
}
|
||||||
|
@ -4,17 +4,30 @@ use std::path::Path;
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::{fs, thread};
|
use std::{fs, thread};
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
use crossbeam_channel::{Receiver, Sender};
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
use heed::types::{Str, Unit};
|
use heed::types::{Str, Unit, SerdeBincode};
|
||||||
use heed::{CompactionOption, Result as ZResult};
|
use heed::CompactionOption;
|
||||||
use log::debug;
|
use log::{debug, error};
|
||||||
use meilisearch_schema::Schema;
|
use meilisearch_schema::Schema;
|
||||||
|
|
||||||
use crate::{store, update, Index, MResult};
|
use crate::{store, update, Index, MResult, Error};
|
||||||
|
|
||||||
pub type BoxUpdateFn = Box<dyn Fn(&str, update::ProcessedUpdateResult) + Send + Sync + 'static>;
|
pub type BoxUpdateFn = Box<dyn Fn(&str, update::ProcessedUpdateResult) + Send + Sync + 'static>;
|
||||||
|
|
||||||
type ArcSwapFn = arc_swap::ArcSwapOption<BoxUpdateFn>;
|
type ArcSwapFn = arc_swap::ArcSwapOption<BoxUpdateFn>;
|
||||||
|
|
||||||
|
type SerdeDatetime = SerdeBincode<DateTime<Utc>>;
|
||||||
|
|
||||||
|
pub type MainWriter<'a> = heed::RwTxn<'a, MainT>;
|
||||||
|
pub type MainReader = heed::RoTxn<MainT>;
|
||||||
|
|
||||||
|
pub type UpdateWriter<'a> = heed::RwTxn<'a, UpdateT>;
|
||||||
|
pub type UpdateReader = heed::RoTxn<UpdateT>;
|
||||||
|
|
||||||
|
const UNHEALTHY_KEY: &str = "_is_unhealthy";
|
||||||
|
const LAST_UPDATE_KEY: &str = "last-update";
|
||||||
|
|
||||||
pub struct MainT;
|
pub struct MainT;
|
||||||
pub struct UpdateT;
|
pub struct UpdateT;
|
||||||
|
|
||||||
@ -241,6 +254,13 @@ impl Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_indexing(&self, reader: &UpdateReader, index: &str) -> MResult<Option<bool>> {
|
||||||
|
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<str>) -> MResult<Index> {
|
pub fn create_index(&self, name: impl AsRef<str>) -> MResult<Index> {
|
||||||
let name = name.as_ref();
|
let name = name.as_ref();
|
||||||
let mut indexes_lock = self.indexes.write().unwrap();
|
let mut indexes_lock = self.indexes.write().unwrap();
|
||||||
@ -319,23 +339,73 @@ impl Database {
|
|||||||
self.update_fn.swap(None);
|
self.update_fn.swap(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_read_txn(&self) -> heed::Result<heed::RoTxn<MainT>> {
|
pub fn main_read_txn(&self) -> MResult<MainReader> {
|
||||||
self.env.typed_read_txn::<MainT>()
|
Ok(self.env.typed_read_txn::<MainT>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_write_txn(&self) -> heed::Result<heed::RwTxn<MainT>> {
|
pub(crate) fn main_write_txn(&self) -> MResult<MainWriter> {
|
||||||
self.env.typed_write_txn::<MainT>()
|
Ok(self.env.typed_write_txn::<MainT>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_read_txn(&self) -> heed::Result<heed::RoTxn<UpdateT>> {
|
/// Calls f providing it with a writer to the main database. After f is called, makes sure the
|
||||||
self.update_env.typed_read_txn::<UpdateT>()
|
/// transaction is commited. Returns whatever result f returns.
|
||||||
|
pub fn main_write<F, R, E>(&self, f: F) -> Result<R, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut MainWriter) -> Result<R, E>,
|
||||||
|
E: From<Error>,
|
||||||
|
{
|
||||||
|
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<heed::RwTxn<UpdateT>> {
|
/// provides a context with a reader to the main database. experimental.
|
||||||
self.update_env.typed_write_txn::<UpdateT>()
|
pub fn main_read<F, R, E>(&self, f: F) -> Result<R, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&MainReader) -> Result<R, E>,
|
||||||
|
E: From<Error>,
|
||||||
|
{
|
||||||
|
let reader = self.main_read_txn()?;
|
||||||
|
let result = f(&reader)?;
|
||||||
|
reader.abort().map_err(Error::Heed)?;
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_and_compact_to_path<P: AsRef<Path>>(&self, path: P) -> ZResult<(File, File)> {
|
pub fn update_read_txn(&self) -> MResult<UpdateReader> {
|
||||||
|
Ok(self.update_env.typed_read_txn::<UpdateT>()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_write_txn(&self) -> MResult<heed::RwTxn<UpdateT>> {
|
||||||
|
Ok(self.update_env.typed_write_txn::<UpdateT>()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<F, R, E>(&self, f: F) -> Result<R, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut UpdateWriter) -> Result<R, E>,
|
||||||
|
E: From<Error>,
|
||||||
|
{
|
||||||
|
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<F, R, E>(&self, f: F) -> Result<R, E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&UpdateReader) -> Result<R, E>,
|
||||||
|
E: From<Error>,
|
||||||
|
{
|
||||||
|
let reader = self.update_read_txn()?;
|
||||||
|
let result = f(&reader)?;
|
||||||
|
reader.abort().map_err(Error::Heed)?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_and_compact_to_path<P: AsRef<Path>>(&self, path: P) -> MResult<(File, File)> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
||||||
let env_path = path.join("main");
|
let env_path = path.join("main");
|
||||||
@ -352,7 +422,7 @@ impl Database {
|
|||||||
Ok(update_env_file) => Ok((env_file, update_env_file)),
|
Ok(update_env_file) => Ok((env_file, update_env_file)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
fs::remove_file(env_path)?;
|
fs::remove_file(env_path)?;
|
||||||
Err(e)
|
Err(e.into())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -362,9 +432,78 @@ impl Database {
|
|||||||
indexes.keys().cloned().collect()
|
indexes.keys().cloned().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn common_store(&self) -> heed::PolyDatabase {
|
pub(crate) fn common_store(&self) -> heed::PolyDatabase {
|
||||||
self.common_store
|
self.common_store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn last_update(&self, reader: &heed::RoTxn<MainT>) -> MResult<Option<DateTime<Utc>>> {
|
||||||
|
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<MainT>, time: &DateTime<Utc>) -> MResult<()> {
|
||||||
|
self.common_store()
|
||||||
|
.put::<_, Str, SerdeDatetime>(writer, LAST_UPDATE_KEY, time)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_healthy(&self, writer: &mut heed::RwTxn<MainT>) -> MResult<()> {
|
||||||
|
let common_store = self.common_store();
|
||||||
|
common_store.delete::<_, Str>(writer, UNHEALTHY_KEY)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_unhealthy(&self, writer: &mut heed::RwTxn<MainT>) -> MResult<()> {
|
||||||
|
let common_store = self.common_store();
|
||||||
|
common_store.put::<_, Str, Unit>(writer, UNHEALTHY_KEY, &())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_health(&self, reader: &heed::RoTxn<MainT>) -> MResult<Option<()>> {
|
||||||
|
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_distribution(writer, &frequency)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -9,6 +9,8 @@ pub use fst::Error as FstError;
|
|||||||
pub use heed::Error as HeedError;
|
pub use heed::Error as HeedError;
|
||||||
pub use pest::error as pest_error;
|
pub use pest::error as pest_error;
|
||||||
|
|
||||||
|
use meilisearch_error::{ErrorCode, Code};
|
||||||
|
|
||||||
pub type MResult<T> = Result<T, Error>;
|
pub type MResult<T> = Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -21,17 +23,41 @@ pub enum Error {
|
|||||||
MissingDocumentId,
|
MissingDocumentId,
|
||||||
MaxFieldsLimitExceeded,
|
MaxFieldsLimitExceeded,
|
||||||
Schema(meilisearch_schema::Error),
|
Schema(meilisearch_schema::Error),
|
||||||
Zlmdb(heed::Error),
|
Heed(heed::Error),
|
||||||
Fst(fst::Error),
|
Fst(fst::Error),
|
||||||
SerdeJson(SerdeJsonError),
|
SerdeJson(SerdeJsonError),
|
||||||
Bincode(bincode::Error),
|
Bincode(bincode::Error),
|
||||||
Serializer(SerializerError),
|
Serializer(SerializerError),
|
||||||
Deserializer(DeserializerError),
|
Deserializer(DeserializerError),
|
||||||
UnsupportedOperation(UnsupportedOperation),
|
|
||||||
FilterParseError(PestError<Rule>),
|
FilterParseError(PestError<Rule>),
|
||||||
FacetError(FacetError),
|
FacetError(FacetError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ErrorCode for Error {
|
||||||
|
fn error_code(&self) -> Code {
|
||||||
|
use Error::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
FacetError(_) => Code::Facet,
|
||||||
|
FilterParseError(_) => Code::Filter,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(error: io::Error) -> Error {
|
fn from(error: io::Error) -> Error {
|
||||||
Error::Io(error)
|
Error::Io(error)
|
||||||
@ -74,7 +100,7 @@ impl From<meilisearch_schema::Error> for Error {
|
|||||||
|
|
||||||
impl From<HeedError> for Error {
|
impl From<HeedError> for Error {
|
||||||
fn from(error: HeedError) -> Error {
|
fn from(error: HeedError) -> Error {
|
||||||
Error::Zlmdb(error)
|
Error::Heed(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,12 +134,6 @@ impl From<DeserializerError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UnsupportedOperation> for Error {
|
|
||||||
fn from(op: UnsupportedOperation) -> Error {
|
|
||||||
Error::UnsupportedOperation(op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use self::Error::*;
|
use self::Error::*;
|
||||||
@ -126,13 +146,12 @@ impl fmt::Display for Error {
|
|||||||
MissingDocumentId => write!(f, "document id is missing"),
|
MissingDocumentId => write!(f, "document id is missing"),
|
||||||
MaxFieldsLimitExceeded => write!(f, "maximum number of fields in a document exceeded"),
|
MaxFieldsLimitExceeded => write!(f, "maximum number of fields in a document exceeded"),
|
||||||
Schema(e) => write!(f, "schema error; {}", e),
|
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),
|
Fst(e) => write!(f, "fst error; {}", e),
|
||||||
SerdeJson(e) => write!(f, "serde json error; {}", e),
|
SerdeJson(e) => write!(f, "serde json error; {}", e),
|
||||||
Bincode(e) => write!(f, "bincode error; {}", e),
|
Bincode(e) => write!(f, "bincode error; {}", e),
|
||||||
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),
|
|
||||||
FilterParseError(e) => write!(f, "error parsing filter; {}", e),
|
FilterParseError(e) => write!(f, "error parsing filter; {}", e),
|
||||||
FacetError(e) => write!(f, "error processing facet filter: {}", e),
|
FacetError(e) => write!(f, "error processing facet filter: {}", e),
|
||||||
}
|
}
|
||||||
@ -141,27 +160,17 @@ impl fmt::Display for Error {
|
|||||||
|
|
||||||
impl error::Error for Error {}
|
impl error::Error for Error {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
struct FilterParseError(PestError<Rule>);
|
||||||
pub enum UnsupportedOperation {
|
|
||||||
SchemaAlreadyExists,
|
|
||||||
CannotUpdateSchemaPrimaryKey,
|
|
||||||
CannotReorderSchemaAttribute,
|
|
||||||
CanOnlyIntroduceNewSchemaAttributesAtEnd,
|
|
||||||
CannotRemoveSchemaAttribute,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for UnsupportedOperation {
|
impl fmt::Display for FilterParseError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use self::UnsupportedOperation::*;
|
use crate::pest_error::LineColLocation::*;
|
||||||
match self {
|
|
||||||
SchemaAlreadyExists => write!(f, "Cannot update index which already have a schema"),
|
let (line, column) = match self.0.line_col {
|
||||||
CannotUpdateSchemaPrimaryKey => write!(f, "Cannot update the primary key of a schema"),
|
Span((line, _), (column, _)) => (line, column),
|
||||||
CannotReorderSchemaAttribute => write!(f, "Cannot reorder the attributes of a schema"),
|
Pos((line, column)) => (line, column),
|
||||||
CanOnlyIntroduceNewSchemaAttributesAtEnd => {
|
};
|
||||||
write!(f, "Can only introduce new attributes at end of a schema")
|
write!(f, "parsing error on line {} at column {}: {}", line, column, self.0.variant.message())
|
||||||
}
|
|
||||||
CannotRemoveSchemaAttribute => write!(f, "Cannot remove attributes from a schema"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ use meilisearch_schema::{FieldId, Schema};
|
|||||||
use meilisearch_types::DocumentId;
|
use meilisearch_types::DocumentId;
|
||||||
|
|
||||||
use crate::database::MainT;
|
use crate::database::MainT;
|
||||||
use crate::error::{FacetError, Error};
|
use crate::error::{FacetError, MResult};
|
||||||
use crate::store::BEU16;
|
use crate::store::BEU16;
|
||||||
|
|
||||||
/// Data structure used to represent a boolean expression in the form of nested arrays.
|
/// Data structure used to represent a boolean expression in the form of nested arrays.
|
||||||
@ -34,14 +34,13 @@ impl FacetFilter {
|
|||||||
s: &str,
|
s: &str,
|
||||||
schema: &Schema,
|
schema: &Schema,
|
||||||
attributes_for_faceting: &[FieldId],
|
attributes_for_faceting: &[FieldId],
|
||||||
) -> Result<Self, FacetError> {
|
) -> MResult<FacetFilter> {
|
||||||
|
|
||||||
let parsed = serde_json::from_str::<Value>(s).map_err(|e| FacetError::ParsingError(e.to_string()))?;
|
let parsed = serde_json::from_str::<Value>(s).map_err(|e| FacetError::ParsingError(e.to_string()))?;
|
||||||
let mut filter = Vec::new();
|
let mut filter = Vec::new();
|
||||||
match parsed {
|
match parsed {
|
||||||
Value::Array(and_exprs) => {
|
Value::Array(and_exprs) => {
|
||||||
if and_exprs.is_empty() {
|
if and_exprs.is_empty() {
|
||||||
return Err(FacetError::EmptyArray);
|
return Err(FacetError::EmptyArray.into());
|
||||||
}
|
}
|
||||||
for expr in and_exprs {
|
for expr in and_exprs {
|
||||||
match expr {
|
match expr {
|
||||||
@ -51,7 +50,7 @@ impl FacetFilter {
|
|||||||
}
|
}
|
||||||
Value::Array(or_exprs) => {
|
Value::Array(or_exprs) => {
|
||||||
if or_exprs.is_empty() {
|
if or_exprs.is_empty() {
|
||||||
return Err(FacetError::EmptyArray);
|
return Err(FacetError::EmptyArray.into());
|
||||||
}
|
}
|
||||||
let mut inner = Vec::new();
|
let mut inner = Vec::new();
|
||||||
for expr in or_exprs {
|
for expr in or_exprs {
|
||||||
@ -60,17 +59,17 @@ impl FacetFilter {
|
|||||||
let key = FacetKey::from_str( &s, schema, attributes_for_faceting)?;
|
let key = FacetKey::from_str( &s, schema, attributes_for_faceting)?;
|
||||||
inner.push(key);
|
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));
|
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));
|
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,
|
index: &crate::Index,
|
||||||
document_ids: &[DocumentId],
|
document_ids: &[DocumentId],
|
||||||
attributes_for_facetting: &[FieldId],
|
attributes_for_facetting: &[FieldId],
|
||||||
) -> Result<HashMap<FacetKey, Vec<DocumentId>>, Error> {
|
) -> MResult<HashMap<FacetKey, Vec<DocumentId>>> {
|
||||||
let mut facet_map = HashMap::new();
|
let mut facet_map = HashMap::new();
|
||||||
for document_id in document_ids {
|
for document_id in document_ids {
|
||||||
for result in index
|
for result in index
|
||||||
@ -210,7 +209,7 @@ pub fn facet_map_from_docs(
|
|||||||
schema: &Schema,
|
schema: &Schema,
|
||||||
documents: &HashMap<DocumentId, IndexMap<String, Value>>,
|
documents: &HashMap<DocumentId, IndexMap<String, Value>>,
|
||||||
attributes_for_facetting: &[FieldId],
|
attributes_for_facetting: &[FieldId],
|
||||||
) -> Result<HashMap<FacetKey, Vec<DocumentId>>, Error> {
|
) -> MResult<HashMap<FacetKey, Vec<DocumentId>>> {
|
||||||
let mut facet_map = HashMap::new();
|
let mut facet_map = HashMap::new();
|
||||||
let attributes_for_facetting = attributes_for_facetting
|
let attributes_for_facetting = attributes_for_facetting
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -26,7 +26,7 @@ pub mod settings;
|
|||||||
pub mod store;
|
pub mod store;
|
||||||
pub mod update;
|
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::error::{Error, HeedError, FstError, MResult, pest_error, FacetError};
|
||||||
pub use self::filters::Filter;
|
pub use self::filters::Filter;
|
||||||
pub use self::number::{Number, ParseNumberError};
|
pub use self::number::{Number, ParseNumberError};
|
||||||
|
@ -268,7 +268,7 @@ mod tests {
|
|||||||
let alternatives = self
|
let alternatives = self
|
||||||
.index
|
.index
|
||||||
.synonyms
|
.synonyms
|
||||||
.synonyms(&writer, word.as_bytes())
|
.synonyms_fst(&writer, word.as_bytes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new = sdset_into_fstset(&new);
|
let new = sdset_into_fstset(&new);
|
||||||
|
@ -147,7 +147,7 @@ fn split_best_frequency<'a>(reader: &heed::RoTxn<MainT>, ctx: &Context, word: &'
|
|||||||
|
|
||||||
fn fetch_synonyms(reader: &heed::RoTxn<MainT>, ctx: &Context, words: &[&str]) -> MResult<Vec<Vec<String>>> {
|
fn fetch_synonyms(reader: &heed::RoTxn<MainT>, ctx: &Context, words: &[&str]) -> MResult<Vec<Vec<String>>> {
|
||||||
let words = normalize_str(&words.join(" "));
|
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 strings = Vec::new();
|
||||||
let mut stream = set.stream();
|
let mut stream = set.stream();
|
||||||
|
@ -4,6 +4,7 @@ use crate::DocumentId;
|
|||||||
use heed::types::OwnedType;
|
use heed::types::OwnedType;
|
||||||
use heed::Result as ZResult;
|
use heed::Result as ZResult;
|
||||||
use meilisearch_schema::IndexedPos;
|
use meilisearch_schema::IndexedPos;
|
||||||
|
use crate::MResult;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct DocumentsFieldsCounts {
|
pub struct DocumentsFieldsCounts {
|
||||||
@ -60,7 +61,7 @@ impl DocumentsFieldsCounts {
|
|||||||
Ok(DocumentFieldsCountsIter { iter })
|
Ok(DocumentFieldsCountsIter { iter })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn documents_ids<'txn>(self, reader: &'txn heed::RoTxn<MainT>) -> ZResult<DocumentsIdsIter<'txn>> {
|
pub fn documents_ids<'txn>(self, reader: &'txn heed::RoTxn<MainT>) -> MResult<DocumentsIdsIter<'txn>> {
|
||||||
let iter = self.documents_fields_counts.iter(reader)?;
|
let iter = self.documents_fields_counts.iter(reader)?;
|
||||||
Ok(DocumentsIdsIter {
|
Ok(DocumentsIdsIter {
|
||||||
last_seen_id: None,
|
last_seen_id: None,
|
||||||
@ -102,7 +103,7 @@ pub struct DocumentsIdsIter<'txn> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for DocumentsIdsIter<'_> {
|
impl Iterator for DocumentsIdsIter<'_> {
|
||||||
type Item = ZResult<DocumentId>;
|
type Item = MResult<DocumentId>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
for result in &mut self.iter {
|
for result in &mut self.iter {
|
||||||
@ -114,7 +115,7 @@ impl Iterator for DocumentsIdsIter<'_> {
|
|||||||
return Some(Ok(document_id));
|
return Some(Ok(document_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -2,14 +2,13 @@ use std::borrow::Cow;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use heed::Result as ZResult;
|
|
||||||
use heed::types::{ByteSlice, OwnedType, SerdeBincode, Str};
|
use heed::types::{ByteSlice, OwnedType, SerdeBincode, Str};
|
||||||
use meilisearch_schema::{FieldId, Schema};
|
use meilisearch_schema::{FieldId, Schema};
|
||||||
use meilisearch_types::DocumentId;
|
use meilisearch_types::DocumentId;
|
||||||
use sdset::Set;
|
use sdset::Set;
|
||||||
|
|
||||||
use crate::database::MainT;
|
use crate::database::MainT;
|
||||||
use crate::RankedMap;
|
use crate::{RankedMap, MResult};
|
||||||
use crate::settings::RankingRule;
|
use crate::settings::RankingRule;
|
||||||
use crate::{FstSetCow, FstMapCow};
|
use crate::{FstSetCow, FstMapCow};
|
||||||
use super::{CowSet, DocumentsIds};
|
use super::{CowSet, DocumentsIds};
|
||||||
@ -41,75 +40,73 @@ pub struct Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Main {
|
impl Main {
|
||||||
pub fn clear(self, writer: &mut heed::RwTxn<MainT>) -> ZResult<()> {
|
pub fn clear(self, writer: &mut heed::RwTxn<MainT>) -> MResult<()> {
|
||||||
self.main.clear(writer)
|
Ok(self.main.clear(writer)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_name(self, writer: &mut heed::RwTxn<MainT>, name: &str) -> ZResult<()> {
|
pub fn put_name(self, writer: &mut heed::RwTxn<MainT>, name: &str) -> MResult<()> {
|
||||||
self.main.put::<_, Str, Str>(writer, NAME_KEY, name)
|
Ok(self.main.put::<_, Str, Str>(writer, NAME_KEY, name)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(self, reader: &heed::RoTxn<MainT>) -> ZResult<Option<String>> {
|
pub fn name(self, reader: &heed::RoTxn<MainT>) -> MResult<Option<String>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.main
|
.main
|
||||||
.get::<_, Str, Str>(reader, NAME_KEY)?
|
.get::<_, Str, Str>(reader, NAME_KEY)?
|
||||||
.map(|name| name.to_owned()))
|
.map(|name| name.to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_created_at(self, writer: &mut heed::RwTxn<MainT>) -> ZResult<()> {
|
pub fn put_created_at(self, writer: &mut heed::RwTxn<MainT>) -> MResult<()> {
|
||||||
self.main
|
Ok(self.main.put::<_, Str, SerdeDatetime>(writer, CREATED_AT_KEY, &Utc::now())?)
|
||||||
.put::<_, Str, SerdeDatetime>(writer, CREATED_AT_KEY, &Utc::now())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn created_at(self, reader: &heed::RoTxn<MainT>) -> ZResult<Option<DateTime<Utc>>> {
|
pub fn created_at(self, reader: &heed::RoTxn<MainT>) -> MResult<Option<DateTime<Utc>>> {
|
||||||
self.main.get::<_, Str, SerdeDatetime>(reader, CREATED_AT_KEY)
|
Ok(self.main.get::<_, Str, SerdeDatetime>(reader, CREATED_AT_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_updated_at(self, writer: &mut heed::RwTxn<MainT>) -> ZResult<()> {
|
pub fn put_updated_at(self, writer: &mut heed::RwTxn<MainT>) -> MResult<()> {
|
||||||
self.main
|
Ok(self.main.put::<_, Str, SerdeDatetime>(writer, UPDATED_AT_KEY, &Utc::now())?)
|
||||||
.put::<_, Str, SerdeDatetime>(writer, UPDATED_AT_KEY, &Utc::now())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn updated_at(self, reader: &heed::RoTxn<MainT>) -> ZResult<Option<DateTime<Utc>>> {
|
pub fn updated_at(self, reader: &heed::RoTxn<MainT>) -> MResult<Option<DateTime<Utc>>> {
|
||||||
self.main.get::<_, Str, SerdeDatetime>(reader, UPDATED_AT_KEY)
|
Ok(self.main.get::<_, Str, SerdeDatetime>(reader, UPDATED_AT_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_internal_docids(self, writer: &mut heed::RwTxn<MainT>, ids: &sdset::Set<DocumentId>) -> ZResult<()> {
|
pub fn put_internal_docids(self, writer: &mut heed::RwTxn<MainT>, ids: &sdset::Set<DocumentId>) -> MResult<()> {
|
||||||
self.main.put::<_, Str, DocumentsIds>(writer, INTERNAL_DOCIDS_KEY, ids)
|
Ok(self.main.put::<_, Str, DocumentsIds>(writer, INTERNAL_DOCIDS_KEY, ids)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn internal_docids<'txn>(self, reader: &'txn heed::RoTxn<MainT>) -> ZResult<Cow<'txn, sdset::Set<DocumentId>>> {
|
pub fn internal_docids<'txn>(self, reader: &'txn heed::RoTxn<MainT>) -> MResult<Cow<'txn, sdset::Set<DocumentId>>> {
|
||||||
match self.main.get::<_, Str, DocumentsIds>(reader, INTERNAL_DOCIDS_KEY)? {
|
match self.main.get::<_, Str, DocumentsIds>(reader, INTERNAL_DOCIDS_KEY)? {
|
||||||
Some(ids) => Ok(ids),
|
Some(ids) => Ok(ids),
|
||||||
None => Ok(Cow::default()),
|
None => Ok(Cow::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge_internal_docids(self, writer: &mut heed::RwTxn<MainT>, new_ids: &sdset::Set<DocumentId>) -> ZResult<()> {
|
pub fn merge_internal_docids(self, writer: &mut heed::RwTxn<MainT>, new_ids: &sdset::Set<DocumentId>) -> MResult<()> {
|
||||||
use sdset::SetOperation;
|
use sdset::SetOperation;
|
||||||
|
|
||||||
// We do an union of the old and new internal ids.
|
// We do an union of the old and new internal ids.
|
||||||
let internal_docids = self.internal_docids(writer)?;
|
let internal_docids = self.internal_docids(writer)?;
|
||||||
let internal_docids = sdset::duo::Union::new(&internal_docids, new_ids).into_set_buf();
|
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<MainT>, ids: &sdset::Set<DocumentId>) -> ZResult<()> {
|
pub fn remove_internal_docids(self, writer: &mut heed::RwTxn<MainT>, ids: &sdset::Set<DocumentId>) -> MResult<()> {
|
||||||
use sdset::SetOperation;
|
use sdset::SetOperation;
|
||||||
|
|
||||||
// We do a difference of the old and new internal ids.
|
// We do a difference of the old and new internal ids.
|
||||||
let internal_docids = self.internal_docids(writer)?;
|
let internal_docids = self.internal_docids(writer)?;
|
||||||
let internal_docids = sdset::duo::Difference::new(&internal_docids, ids).into_set_buf();
|
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<A>(self, writer: &mut heed::RwTxn<MainT>, ids: &fst::Map<A>) -> ZResult<()>
|
pub fn put_external_docids<A>(self, writer: &mut heed::RwTxn<MainT>, ids: &fst::Map<A>) -> MResult<()>
|
||||||
where A: AsRef<[u8]>,
|
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<A>(self, writer: &mut heed::RwTxn<MainT>, new_docids: &fst::Map<A>) -> ZResult<()>
|
pub fn merge_external_docids<A>(self, writer: &mut heed::RwTxn<MainT>, new_docids: &fst::Map<A>) -> MResult<()>
|
||||||
where A: AsRef<[u8]>,
|
where A: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
use fst::{Streamer, IntoStreamer};
|
use fst::{Streamer, IntoStreamer};
|
||||||
@ -124,10 +121,10 @@ impl Main {
|
|||||||
drop(op);
|
drop(op);
|
||||||
|
|
||||||
let external_docids = build.into_map();
|
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<A>(self, writer: &mut heed::RwTxn<MainT>, ids: &fst::Map<A>) -> ZResult<()>
|
pub fn remove_external_docids<A>(self, writer: &mut heed::RwTxn<MainT>, ids: &fst::Map<A>) -> MResult<()>
|
||||||
where A: AsRef<[u8]>,
|
where A: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
use fst::{Streamer, IntoStreamer};
|
use fst::{Streamer, IntoStreamer};
|
||||||
@ -145,74 +142,90 @@ impl Main {
|
|||||||
self.put_external_docids(writer, &external_docids)
|
self.put_external_docids(writer, &external_docids)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn external_docids(self, reader: &heed::RoTxn<MainT>) -> ZResult<FstMapCow> {
|
pub fn external_docids(self, reader: &heed::RoTxn<MainT>) -> MResult<FstMapCow> {
|
||||||
match self.main.get::<_, Str, ByteSlice>(reader, EXTERNAL_DOCIDS_KEY)? {
|
match self.main.get::<_, Str, ByteSlice>(reader, EXTERNAL_DOCIDS_KEY)? {
|
||||||
Some(bytes) => Ok(fst::Map::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
Some(bytes) => Ok(fst::Map::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
||||||
None => Ok(fst::Map::default().map_data(Cow::Owned).unwrap()),
|
None => Ok(fst::Map::default().map_data(Cow::Owned).unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn external_to_internal_docid(self, reader: &heed::RoTxn<MainT>, external_docid: &str) -> ZResult<Option<DocumentId>> {
|
pub fn external_to_internal_docid(self, reader: &heed::RoTxn<MainT>, external_docid: &str) -> MResult<Option<DocumentId>> {
|
||||||
let external_ids = self.external_docids(reader)?;
|
let external_ids = self.external_docids(reader)?;
|
||||||
Ok(external_ids.get(external_docid).map(|id| DocumentId(id as u32)))
|
Ok(external_ids.get(external_docid).map(|id| DocumentId(id as u32)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_words_fst<A: AsRef<[u8]>>(self, writer: &mut heed::RwTxn<MainT>, fst: &fst::Set<A>) -> ZResult<()> {
|
pub fn words_fst(self, reader: &heed::RoTxn<MainT>) -> MResult<FstSetCow> {
|
||||||
self.main.put::<_, Str, ByteSlice>(writer, WORDS_KEY, fst.as_fst().as_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn words_fst(self, reader: &heed::RoTxn<MainT>) -> ZResult<FstSetCow> {
|
|
||||||
match self.main.get::<_, Str, ByteSlice>(reader, WORDS_KEY)? {
|
match self.main.get::<_, Str, ByteSlice>(reader, WORDS_KEY)? {
|
||||||
Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
||||||
None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()),
|
None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_schema(self, writer: &mut heed::RwTxn<MainT>, schema: &Schema) -> ZResult<()> {
|
pub fn put_words_fst<A: AsRef<[u8]>>(self, writer: &mut heed::RwTxn<MainT>, fst: &fst::Set<A>) -> MResult<()> {
|
||||||
self.main.put::<_, Str, SerdeBincode<Schema>>(writer, SCHEMA_KEY, schema)
|
Ok(self.main.put::<_, Str, ByteSlice>(writer, WORDS_KEY, fst.as_fst().as_bytes())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn schema(self, reader: &heed::RoTxn<MainT>) -> ZResult<Option<Schema>> {
|
pub fn put_schema(self, writer: &mut heed::RwTxn<MainT>, schema: &Schema) -> MResult<()> {
|
||||||
self.main.get::<_, Str, SerdeBincode<Schema>>(reader, SCHEMA_KEY)
|
Ok(self.main.put::<_, Str, SerdeBincode<Schema>>(writer, SCHEMA_KEY, schema)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_schema(self, writer: &mut heed::RwTxn<MainT>) -> ZResult<bool> {
|
pub fn schema(self, reader: &heed::RoTxn<MainT>) -> MResult<Option<Schema>> {
|
||||||
self.main.delete::<_, Str>(writer, SCHEMA_KEY)
|
Ok(self.main.get::<_, Str, SerdeBincode<Schema>>(reader, SCHEMA_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_ranked_map(self, writer: &mut heed::RwTxn<MainT>, ranked_map: &RankedMap) -> ZResult<()> {
|
pub fn delete_schema(self, writer: &mut heed::RwTxn<MainT>) -> MResult<bool> {
|
||||||
self.main.put::<_, Str, SerdeBincode<RankedMap>>(writer, RANKED_MAP_KEY, &ranked_map)
|
Ok(self.main.delete::<_, Str>(writer, SCHEMA_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ranked_map(self, reader: &heed::RoTxn<MainT>) -> ZResult<Option<RankedMap>> {
|
pub fn put_ranked_map(self, writer: &mut heed::RwTxn<MainT>, ranked_map: &RankedMap) -> MResult<()> {
|
||||||
self.main.get::<_, Str, SerdeBincode<RankedMap>>(reader, RANKED_MAP_KEY)
|
Ok(self.main.put::<_, Str, SerdeBincode<RankedMap>>(writer, RANKED_MAP_KEY, &ranked_map)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_synonyms_fst<A: AsRef<[u8]>>(self, writer: &mut heed::RwTxn<MainT>, fst: &fst::Set<A>) -> ZResult<()> {
|
pub fn ranked_map(self, reader: &heed::RoTxn<MainT>) -> MResult<Option<RankedMap>> {
|
||||||
|
Ok(self.main.get::<_, Str, SerdeBincode<RankedMap>>(reader, RANKED_MAP_KEY)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_synonyms_fst<A: AsRef<[u8]>>(self, writer: &mut heed::RwTxn<MainT>, fst: &fst::Set<A>) -> MResult<()> {
|
||||||
let bytes = fst.as_fst().as_bytes();
|
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<MainT>) -> ZResult<FstSetCow> {
|
pub(crate) fn synonyms_fst(self, reader: &heed::RoTxn<MainT>) -> MResult<FstSetCow> {
|
||||||
match self.main.get::<_, Str, ByteSlice>(reader, SYNONYMS_KEY)? {
|
match self.main.get::<_, Str, ByteSlice>(reader, SYNONYMS_KEY)? {
|
||||||
Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
||||||
None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()),
|
None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_stop_words_fst<A: AsRef<[u8]>>(self, writer: &mut heed::RwTxn<MainT>, fst: &fst::Set<A>) -> ZResult<()> {
|
pub fn synonyms(self, reader: &heed::RoTxn<MainT>) -> MResult<Vec<String>> {
|
||||||
let bytes = fst.as_fst().as_bytes();
|
let synonyms = self
|
||||||
self.main.put::<_, Str, ByteSlice>(writer, STOP_WORDS_KEY, bytes)
|
.synonyms_fst(&reader)?
|
||||||
|
.stream()
|
||||||
|
.into_strs()?;
|
||||||
|
Ok(synonyms)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_words_fst(self, reader: &heed::RoTxn<MainT>) -> ZResult<FstSetCow> {
|
pub fn put_stop_words_fst<A: AsRef<[u8]>>(self, writer: &mut heed::RwTxn<MainT>, fst: &fst::Set<A>) -> 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<MainT>) -> MResult<FstSetCow> {
|
||||||
match self.main.get::<_, Str, ByteSlice>(reader, STOP_WORDS_KEY)? {
|
match self.main.get::<_, Str, ByteSlice>(reader, STOP_WORDS_KEY)? {
|
||||||
Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
||||||
None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()),
|
None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_number_of_documents<F>(self, writer: &mut heed::RwTxn<MainT>, f: F) -> ZResult<u64>
|
pub fn stop_words(self, reader: &heed::RoTxn<MainT>) -> MResult<Vec<String>> {
|
||||||
|
let stop_word_list = self
|
||||||
|
.stop_words_fst(reader)?
|
||||||
|
.stream()
|
||||||
|
.into_strs()?;
|
||||||
|
Ok(stop_word_list)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_number_of_documents<F>(self, writer: &mut heed::RwTxn<MainT>, f: F) -> MResult<u64>
|
||||||
where
|
where
|
||||||
F: Fn(u64) -> u64,
|
F: Fn(u64) -> u64,
|
||||||
{
|
{
|
||||||
@ -222,11 +235,10 @@ impl Main {
|
|||||||
Ok(new)
|
Ok(new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn number_of_documents(self, reader: &heed::RoTxn<MainT>) -> ZResult<u64> {
|
pub fn number_of_documents(self, reader: &heed::RoTxn<MainT>) -> MResult<u64> {
|
||||||
match self
|
match self
|
||||||
.main
|
.main
|
||||||
.get::<_, Str, OwnedType<u64>>(reader, NUMBER_OF_DOCUMENTS_KEY)?
|
.get::<_, Str, OwnedType<u64>>(reader, NUMBER_OF_DOCUMENTS_KEY)? {
|
||||||
{
|
|
||||||
Some(value) => Ok(value),
|
Some(value) => Ok(value),
|
||||||
None => Ok(0),
|
None => Ok(0),
|
||||||
}
|
}
|
||||||
@ -235,13 +247,12 @@ impl Main {
|
|||||||
pub fn put_fields_distribution(
|
pub fn put_fields_distribution(
|
||||||
self,
|
self,
|
||||||
writer: &mut heed::RwTxn<MainT>,
|
writer: &mut heed::RwTxn<MainT>,
|
||||||
fields_distribution: &FreqsMap,
|
fields_frequency: &FreqsMap,
|
||||||
) -> ZResult<()> {
|
) -> MResult<()> {
|
||||||
self.main
|
Ok(self.main.put::<_, Str, SerdeFreqsMap>(writer, FIELDS_DISTRIBUTION_KEY, fields_frequency)?)
|
||||||
.put::<_, Str, SerdeFreqsMap>(writer, FIELDS_DISTRIBUTION_KEY, fields_distribution)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fields_distribution(&self, reader: &heed::RoTxn<MainT>) -> ZResult<Option<FreqsMap>> {
|
pub fn fields_distribution(&self, reader: &heed::RoTxn<MainT>) -> MResult<Option<FreqsMap>> {
|
||||||
match self
|
match self
|
||||||
.main
|
.main
|
||||||
.get::<_, Str, SerdeFreqsMap>(reader, FIELDS_DISTRIBUTION_KEY)?
|
.get::<_, Str, SerdeFreqsMap>(reader, FIELDS_DISTRIBUTION_KEY)?
|
||||||
@ -251,51 +262,50 @@ impl Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attributes_for_faceting<'txn>(&self, reader: &'txn heed::RoTxn<MainT>) -> ZResult<Option<Cow<'txn, Set<FieldId>>>> {
|
pub fn attributes_for_faceting<'txn>(&self, reader: &'txn heed::RoTxn<MainT>) -> MResult<Option<Cow<'txn, Set<FieldId>>>> {
|
||||||
self.main.get::<_, Str, CowSet<FieldId>>(reader, ATTRIBUTES_FOR_FACETING_KEY)
|
Ok(self.main.get::<_, Str, CowSet<FieldId>>(reader, ATTRIBUTES_FOR_FACETING_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_attributes_for_faceting(self, writer: &mut heed::RwTxn<MainT>, attributes: &Set<FieldId>) -> ZResult<()> {
|
pub fn put_attributes_for_faceting(self, writer: &mut heed::RwTxn<MainT>, attributes: &Set<FieldId>) -> MResult<()> {
|
||||||
self.main.put::<_, Str, CowSet<FieldId>>(writer, ATTRIBUTES_FOR_FACETING_KEY, attributes)
|
Ok(self.main.put::<_, Str, CowSet<FieldId>>(writer, ATTRIBUTES_FOR_FACETING_KEY, attributes)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_attributes_for_faceting(self, writer: &mut heed::RwTxn<MainT>) -> ZResult<bool> {
|
pub fn delete_attributes_for_faceting(self, writer: &mut heed::RwTxn<MainT>) -> MResult<bool> {
|
||||||
self.main.delete::<_, Str>(writer, ATTRIBUTES_FOR_FACETING_KEY)
|
Ok(self.main.delete::<_, Str>(writer, ATTRIBUTES_FOR_FACETING_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ranking_rules(&self, reader: &heed::RoTxn<MainT>) -> ZResult<Option<Vec<RankingRule>>> {
|
pub fn ranking_rules(&self, reader: &heed::RoTxn<MainT>) -> MResult<Option<Vec<RankingRule>>> {
|
||||||
self.main.get::<_, Str, SerdeBincode<Vec<RankingRule>>>(reader, RANKING_RULES_KEY)
|
Ok(self.main.get::<_, Str, SerdeBincode<Vec<RankingRule>>>(reader, RANKING_RULES_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_ranking_rules(self, writer: &mut heed::RwTxn<MainT>, value: &[RankingRule]) -> ZResult<()> {
|
pub fn put_ranking_rules(self, writer: &mut heed::RwTxn<MainT>, value: &[RankingRule]) -> MResult<()> {
|
||||||
self.main.put::<_, Str, SerdeBincode<Vec<RankingRule>>>(writer, RANKING_RULES_KEY, &value.to_vec())
|
Ok(self.main.put::<_, Str, SerdeBincode<Vec<RankingRule>>>(writer, RANKING_RULES_KEY, &value.to_vec())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_ranking_rules(self, writer: &mut heed::RwTxn<MainT>) -> ZResult<bool> {
|
pub fn delete_ranking_rules(self, writer: &mut heed::RwTxn<MainT>) -> MResult<bool> {
|
||||||
self.main.delete::<_, Str>(writer, RANKING_RULES_KEY)
|
Ok(self.main.delete::<_, Str>(writer, RANKING_RULES_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn distinct_attribute(&self, reader: &heed::RoTxn<MainT>) -> ZResult<Option<String>> {
|
pub fn distinct_attribute(&self, reader: &heed::RoTxn<MainT>) -> MResult<Option<String>> {
|
||||||
if let Some(value) = self.main.get::<_, Str, Str>(reader, DISTINCT_ATTRIBUTE_KEY)? {
|
if let Some(value) = self.main.get::<_, Str, Str>(reader, DISTINCT_ATTRIBUTE_KEY)? {
|
||||||
return Ok(Some(value.to_owned()))
|
return Ok(Some(value.to_owned()))
|
||||||
}
|
}
|
||||||
return Ok(None)
|
return Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_distinct_attribute(self, writer: &mut heed::RwTxn<MainT>, value: &str) -> ZResult<()> {
|
pub fn put_distinct_attribute(self, writer: &mut heed::RwTxn<MainT>, value: &str) -> MResult<()> {
|
||||||
self.main.put::<_, Str, Str>(writer, DISTINCT_ATTRIBUTE_KEY, value)
|
Ok(self.main.put::<_, Str, Str>(writer, DISTINCT_ATTRIBUTE_KEY, value)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_distinct_attribute(self, writer: &mut heed::RwTxn<MainT>) -> ZResult<bool> {
|
pub fn delete_distinct_attribute(self, writer: &mut heed::RwTxn<MainT>) -> MResult<bool> {
|
||||||
self.main.delete::<_, Str>(writer, DISTINCT_ATTRIBUTE_KEY)
|
Ok(self.main.delete::<_, Str>(writer, DISTINCT_ATTRIBUTE_KEY)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put_customs(self, writer: &mut heed::RwTxn<MainT>, customs: &[u8]) -> ZResult<()> {
|
pub fn put_customs(self, writer: &mut heed::RwTxn<MainT>, customs: &[u8]) -> MResult<()> {
|
||||||
self.main
|
Ok(self.main.put::<_, Str, ByteSlice>(writer, CUSTOMS_KEY, customs)?)
|
||||||
.put::<_, Str, ByteSlice>(writer, CUSTOMS_KEY, customs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn customs<'txn>(self, reader: &'txn heed::RoTxn<MainT>) -> ZResult<Option<&'txn [u8]>> {
|
pub fn customs<'txn>(self, reader: &'txn heed::RoTxn<MainT>) -> MResult<Option<&'txn [u8]>> {
|
||||||
self.main.get::<_, Str, ByteSlice>(reader, CUSTOMS_KEY)
|
Ok(self.main.get::<_, Str, ByteSlice>(reader, CUSTOMS_KEY)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ use std::collections::HashSet;
|
|||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
|
||||||
use heed::Result as ZResult;
|
|
||||||
use heed::{BytesEncode, BytesDecode};
|
use heed::{BytesEncode, BytesDecode};
|
||||||
use meilisearch_schema::{IndexedPos, FieldId};
|
use meilisearch_schema::{IndexedPos, FieldId};
|
||||||
use sdset::{Set, SetBuf};
|
use sdset::{Set, SetBuf};
|
||||||
@ -279,14 +278,14 @@ impl Index {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn customs_update(&self, writer: &mut heed::RwTxn<UpdateT>, customs: Vec<u8>) -> ZResult<u64> {
|
pub fn customs_update(&self, writer: &mut heed::RwTxn<UpdateT>, customs: Vec<u8>) -> MResult<u64> {
|
||||||
let _ = self.updates_notifier.send(UpdateEvent::NewUpdate);
|
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<UpdateT>, update: SettingsUpdate) -> ZResult<u64> {
|
pub fn settings_update(&self, writer: &mut heed::RwTxn<UpdateT>, update: SettingsUpdate) -> MResult<u64> {
|
||||||
let _ = self.updates_notifier.send(UpdateEvent::NewUpdate);
|
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<D>(&self) -> update::DocumentsAddition<D> {
|
pub fn documents_addition<D>(&self) -> update::DocumentsAddition<D> {
|
||||||
|
@ -4,7 +4,7 @@ use heed::Result as ZResult;
|
|||||||
use heed::types::ByteSlice;
|
use heed::types::ByteSlice;
|
||||||
|
|
||||||
use crate::database::MainT;
|
use crate::database::MainT;
|
||||||
use crate::FstSetCow;
|
use crate::{FstSetCow, MResult};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Synonyms {
|
pub struct Synonyms {
|
||||||
@ -27,10 +27,18 @@ impl Synonyms {
|
|||||||
self.synonyms.clear(writer)
|
self.synonyms.clear(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn synonyms<'txn>(self, reader: &'txn heed::RoTxn<MainT>, word: &[u8]) -> ZResult<FstSetCow<'txn>> {
|
pub(crate) fn synonyms_fst<'txn>(self, reader: &'txn heed::RoTxn<MainT>, word: &[u8]) -> ZResult<FstSetCow<'txn>> {
|
||||||
match self.synonyms.get(reader, word)? {
|
match self.synonyms.get(reader, word)? {
|
||||||
Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
Some(bytes) => Ok(fst::Set::new(bytes).unwrap().map_data(Cow::Borrowed).unwrap()),
|
||||||
None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()),
|
None => Ok(fst::Set::default().map_data(Cow::Owned).unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn synonyms(self, reader: &heed::RoTxn<MainT>, word: &[u8]) -> MResult<Vec<String>> {
|
||||||
|
let synonyms = self
|
||||||
|
.synonyms_fst(&reader, word)?
|
||||||
|
.stream()
|
||||||
|
.into_strs()?;
|
||||||
|
Ok(synonyms)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
use heed::Result as ZResult;
|
|
||||||
|
|
||||||
use crate::database::{MainT, UpdateT};
|
use crate::database::{MainT, UpdateT};
|
||||||
use crate::store;
|
use crate::{store, MResult};
|
||||||
use crate::update::{next_update_id, Update};
|
use crate::update::{next_update_id, Update};
|
||||||
|
|
||||||
pub fn apply_customs_update(
|
pub fn apply_customs_update(
|
||||||
writer: &mut heed::RwTxn<MainT>,
|
writer: &mut heed::RwTxn<MainT>,
|
||||||
main_store: store::Main,
|
main_store: store::Main,
|
||||||
customs: &[u8],
|
customs: &[u8],
|
||||||
) -> ZResult<()> {
|
) -> MResult<()> {
|
||||||
main_store.put_customs(writer, customs)
|
main_store.put_customs(writer, customs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,7 +16,7 @@ pub fn push_customs_update(
|
|||||||
updates_store: store::Updates,
|
updates_store: store::Updates,
|
||||||
updates_results_store: store::UpdatesResults,
|
updates_results_store: store::UpdatesResults,
|
||||||
customs: Vec<u8>,
|
customs: Vec<u8>,
|
||||||
) -> ZResult<u64> {
|
) -> MResult<u64> {
|
||||||
let last_update_id = next_update_id(writer, updates_store, updates_results_store)?;
|
let last_update_id = next_update_id(writer, updates_store, updates_results_store)?;
|
||||||
|
|
||||||
let update = Update::customs(customs);
|
let update = Update::customs(customs);
|
||||||
|
8
meilisearch-error/Cargo.toml
Normal file
8
meilisearch-error/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "meilisearch-error"
|
||||||
|
version = "0.10.1"
|
||||||
|
authors = ["marin <postma.marin@protonmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-http = "1.0.1"
|
174
meilisearch-error/src/lib.rs
Normal file
174
meilisearch-error/src/lib.rs
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
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().type_()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// index related error
|
||||||
|
CreateIndex,
|
||||||
|
IndexAlreadyExists,
|
||||||
|
IndexNotFound,
|
||||||
|
InvalidIndexUid,
|
||||||
|
OpenIndex,
|
||||||
|
|
||||||
|
// invalid state error
|
||||||
|
InvalidState,
|
||||||
|
MissingPrimaryKey,
|
||||||
|
PrimaryKeyAlreadyPresent,
|
||||||
|
|
||||||
|
MaxFieldsLimitExceeded,
|
||||||
|
MissingDocumentId,
|
||||||
|
|
||||||
|
Facet,
|
||||||
|
Filter,
|
||||||
|
|
||||||
|
BadParameter,
|
||||||
|
BadRequest,
|
||||||
|
DocumentNotFound,
|
||||||
|
Internal,
|
||||||
|
InvalidToken,
|
||||||
|
Maintenance,
|
||||||
|
MissingAuthorizationHeader,
|
||||||
|
MissingHeader,
|
||||||
|
NotFound,
|
||||||
|
PayloadTooLarge,
|
||||||
|
RetrieveDocument,
|
||||||
|
SearchDocuments,
|
||||||
|
UnsupportedMediaType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Code {
|
||||||
|
|
||||||
|
/// ascociate a `Code` variant to the actual ErrCode
|
||||||
|
fn err_code(&self) -> ErrCode {
|
||||||
|
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::NOT_FOUND), 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),
|
||||||
|
MissingPrimaryKey => ErrCode::internal("missing_primary_key", StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
PrimaryKeyAlreadyPresent => ErrCode::internal("primary_key_already_present", StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
|
||||||
|
// invalid document
|
||||||
|
MaxFieldsLimitExceeded => ErrCode::invalid("max_field_limit_exceeded", StatusCode::BAD_REQUEST),
|
||||||
|
MissingDocumentId => ErrCode::invalid("missing_document_id", 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),
|
||||||
|
DocumentNotFound => ErrCode::internal("document_not_found", StatusCode::NOT_FOUND),
|
||||||
|
Internal => ErrCode::internal("internal", StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the HTTP status code ascociated with the `Code`
|
||||||
|
fn http(&self) -> StatusCode {
|
||||||
|
self.err_code().status_code
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return error name, used as error code
|
||||||
|
fn name(&self) -> String {
|
||||||
|
self.err_code().error_name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the error type
|
||||||
|
fn type_(&self) -> String {
|
||||||
|
self.err_code().error_type.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the doc url ascociated with the error
|
||||||
|
fn url(&self) -> String {
|
||||||
|
format!("https://docs.meilisearch.com/error/{}", self.name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal structure providing a convenient way to create error codes
|
||||||
|
struct ErrCode {
|
||||||
|
status_code: StatusCode,
|
||||||
|
error_type: ErrorType,
|
||||||
|
error_name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrCode {
|
||||||
|
fn authentication(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::Authentication,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::InternalError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid(error_name: &'static str, status_code: StatusCode) -> ErrCode {
|
||||||
|
ErrCode {
|
||||||
|
status_code,
|
||||||
|
error_name,
|
||||||
|
error_type: ErrorType::InvalidRequest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -29,12 +29,12 @@ chrono = { version = "0.4.11", features = ["serde"] }
|
|||||||
crossbeam-channel = "0.4.2"
|
crossbeam-channel = "0.4.2"
|
||||||
env_logger = "0.7.1"
|
env_logger = "0.7.1"
|
||||||
futures = "0.3.4"
|
futures = "0.3.4"
|
||||||
heed = "0.8.0"
|
|
||||||
http = "0.1.19"
|
http = "0.1.19"
|
||||||
indexmap = { version = "1.3.2", features = ["serde-1"] }
|
indexmap = { version = "1.3.2", features = ["serde-1"] }
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
main_error = "0.1.0"
|
main_error = "0.1.0"
|
||||||
meilisearch-core = { path = "../meilisearch-core", version = "0.10.1" }
|
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-schema = { path = "../meilisearch-schema", version = "0.10.1" }
|
||||||
meilisearch-tokenizer = {path = "../meilisearch-tokenizer", version = "0.10.1"}
|
meilisearch-tokenizer = {path = "../meilisearch-tokenizer", version = "0.10.1"}
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
|
@ -1,21 +1,13 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use meilisearch_core::{Database, DatabaseOptions};
|
||||||
use heed::types::{SerdeBincode, Str};
|
|
||||||
use log::error;
|
|
||||||
use meilisearch_core::{Database, DatabaseOptions, Error as MError, MResult, MainT, UpdateT};
|
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
use sysinfo::Pid;
|
use sysinfo::Pid;
|
||||||
|
|
||||||
use crate::index_update_callback;
|
use crate::index_update_callback;
|
||||||
use crate::option::Opt;
|
use crate::option::Opt;
|
||||||
|
|
||||||
const LAST_UPDATE_KEY: &str = "last-update";
|
|
||||||
|
|
||||||
type SerdeDatetime = SerdeBincode<DateTime<Utc>>;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
inner: Arc<DataInner>,
|
inner: Arc<DataInner>,
|
||||||
@ -62,72 +54,6 @@ impl ApiKeys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataInner {
|
|
||||||
pub fn is_indexing(&self, reader: &heed::RoTxn<UpdateT>, index: &str) -> MResult<Option<bool>> {
|
|
||||||
match self.db.open_index(&index) {
|
|
||||||
Some(index) => index.current_update_id(&reader).map(|u| Some(u.is_some())),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_update(&self, reader: &heed::RoTxn<MainT>) -> MResult<Option<DateTime<Utc>>> {
|
|
||||||
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<MainT>) -> 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<MainT>, 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)
|
|
||||||
.map_err(MError::Zlmdb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
pub fn new(opt: Opt) -> Data {
|
pub fn new(opt: Opt) -> Data {
|
||||||
let db_path = opt.db_path.clone();
|
let db_path = opt.db_path.clone();
|
||||||
|
@ -1,13 +1,41 @@
|
|||||||
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use actix_http::ResponseBuilder;
|
use actix_http::ResponseBuilder;
|
||||||
use actix_web as aweb;
|
use actix_web as aweb;
|
||||||
|
use actix_web::error::JsonPayloadError;
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use actix_web::error::JsonPayloadError;
|
|
||||||
|
use meilisearch_error::{ErrorCode, Code};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ResponseError {
|
pub struct ResponseError {
|
||||||
|
inner: Box<dyn ErrorCode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for ResponseError {
|
||||||
|
fn from(error: Error) -> ResponseError {
|
||||||
|
ResponseError { inner: Box::new(error) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
BadParameter(String, String),
|
BadParameter(String, String),
|
||||||
BadRequest(String),
|
BadRequest(String),
|
||||||
CreateIndex(String),
|
CreateIndex(String),
|
||||||
@ -21,15 +49,40 @@ pub enum ResponseError {
|
|||||||
MissingHeader(String),
|
MissingHeader(String),
|
||||||
NotFound(String),
|
NotFound(String),
|
||||||
OpenIndex(String),
|
OpenIndex(String),
|
||||||
FilterParsing(String),
|
|
||||||
RetrieveDocument(u32, String),
|
RetrieveDocument(u32, String),
|
||||||
SearchDocuments(String),
|
SearchDocuments(String),
|
||||||
PayloadTooLarge,
|
PayloadTooLarge,
|
||||||
UnsupportedMediaType,
|
UnsupportedMediaType,
|
||||||
FacetExpression(String),
|
|
||||||
FacetCount(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {}
|
||||||
|
|
||||||
|
impl ErrorCode for Error {
|
||||||
|
fn error_code(&self) -> Code {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum FacetCountError {
|
pub enum FacetCountError {
|
||||||
AttributeNotSet(String),
|
AttributeNotSet(String),
|
||||||
SyntaxError(String),
|
SyntaxError(String),
|
||||||
@ -37,6 +90,14 @@ pub enum FacetCountError {
|
|||||||
NoFacetSet,
|
NoFacetSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl error::Error for FacetCountError {}
|
||||||
|
|
||||||
|
impl ErrorCode for FacetCountError {
|
||||||
|
fn error_code(&self) -> Code {
|
||||||
|
Code::BadRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FacetCountError {
|
impl FacetCountError {
|
||||||
pub fn unexpected_token(found: impl ToString, expected: &'static [&'static str]) -> FacetCountError {
|
pub fn unexpected_token(found: impl ToString, expected: &'static [&'static str]) -> FacetCountError {
|
||||||
let found = found.to_string();
|
let found = found.to_string();
|
||||||
@ -63,69 +124,69 @@ impl fmt::Display for FacetCountError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError {
|
impl Error {
|
||||||
pub fn internal(err: impl fmt::Display) -> ResponseError {
|
pub fn internal(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::Internal(err.to_string())
|
Error::Internal(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bad_request(err: impl fmt::Display) -> ResponseError {
|
pub fn bad_request(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::BadRequest(err.to_string())
|
Error::BadRequest(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn missing_authorization_header() -> ResponseError {
|
pub fn missing_authorization_header() -> Error {
|
||||||
ResponseError::MissingAuthorizationHeader
|
Error::MissingAuthorizationHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalid_token(err: impl fmt::Display) -> ResponseError {
|
pub fn invalid_token(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::InvalidToken(err.to_string())
|
Error::InvalidToken(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not_found(err: impl fmt::Display) -> ResponseError {
|
pub fn not_found(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::NotFound(err.to_string())
|
Error::NotFound(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_not_found(err: impl fmt::Display) -> ResponseError {
|
pub fn index_not_found(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::IndexNotFound(err.to_string())
|
Error::IndexNotFound(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn document_not_found(err: impl fmt::Display) -> ResponseError {
|
pub fn document_not_found(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::DocumentNotFound(err.to_string())
|
Error::DocumentNotFound(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn missing_header(err: impl fmt::Display) -> ResponseError {
|
pub fn missing_header(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::MissingHeader(err.to_string())
|
Error::MissingHeader(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bad_parameter(param: impl fmt::Display, err: impl fmt::Display) -> ResponseError {
|
pub fn bad_parameter(param: impl fmt::Display, err: impl fmt::Display) -> Error {
|
||||||
ResponseError::BadParameter(param.to_string(), err.to_string())
|
Error::BadParameter(param.to_string(), err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_index(err: impl fmt::Display) -> ResponseError {
|
pub fn open_index(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::OpenIndex(err.to_string())
|
Error::OpenIndex(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_index(err: impl fmt::Display) -> ResponseError {
|
pub fn create_index(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::CreateIndex(err.to_string())
|
Error::CreateIndex(err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalid_index_uid() -> ResponseError {
|
pub fn invalid_index_uid() -> Error {
|
||||||
ResponseError::InvalidIndexUid
|
Error::InvalidIndexUid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maintenance() -> ResponseError {
|
pub fn maintenance() -> Error {
|
||||||
ResponseError::Maintenance
|
Error::Maintenance
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn retrieve_document(doc_id: u32, err: impl fmt::Display) -> ResponseError {
|
pub fn retrieve_document(doc_id: u32, err: impl fmt::Display) -> Error {
|
||||||
ResponseError::RetrieveDocument(doc_id, err.to_string())
|
Error::RetrieveDocument(doc_id, err.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_documents(err: impl fmt::Display) -> ResponseError {
|
pub fn search_documents(err: impl fmt::Display) -> Error {
|
||||||
ResponseError::SearchDocuments(err.to_string())
|
Error::SearchDocuments(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ResponseError {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err),
|
Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err),
|
||||||
@ -137,17 +198,14 @@ impl fmt::Display for ResponseError {
|
|||||||
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::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::InvalidToken(err) => write!(f, "Invalid API key: {}", err),
|
||||||
Self::Maintenance => f.write_str("Server is in maintenance, please try again later"),
|
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::MissingAuthorizationHeader => f.write_str("You must have an authorization token"),
|
||||||
Self::MissingHeader(header) => write!(f, "Header {} is missing", header),
|
Self::MissingHeader(header) => write!(f, "Header {} is missing", header),
|
||||||
Self::NotFound(err) => write!(f, "{} not found", err),
|
Self::NotFound(err) => write!(f, "{} not found", err),
|
||||||
Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", 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::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::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::PayloadTooLarge => f.write_str("Payload to large"),
|
||||||
Self::UnsupportedMediaType => f.write_str("Unsupported media type"),
|
Self::UnsupportedMediaType => f.write_str("Unsupported media type"),
|
||||||
Self::FacetCount(e) => write!(f, "error with facet count: {}", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,101 +214,53 @@ impl aweb::error::ResponseError for ResponseError {
|
|||||||
fn error_response(&self) -> aweb::HttpResponse {
|
fn error_response(&self) -> aweb::HttpResponse {
|
||||||
ResponseBuilder::new(self.status_code()).json(json!({
|
ResponseBuilder::new(self.status_code()).json(json!({
|
||||||
"message": self.to_string(),
|
"message": self.to_string(),
|
||||||
|
"errorCode": self.error_name(),
|
||||||
|
"errorType": self.error_type(),
|
||||||
|
"errorLink": self.error_url(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match *self {
|
self.http_status()
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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::FacetError> for ResponseError {
|
|
||||||
fn from(error: meilisearch_core::FacetError) -> ResponseError {
|
|
||||||
ResponseError::FacetExpression(error.to_string())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<meilisearch_core::Error> for ResponseError {
|
impl From<meilisearch_core::Error> for ResponseError {
|
||||||
fn from(err: meilisearch_core::Error) -> ResponseError {
|
fn from(err: meilisearch_core::Error) -> ResponseError {
|
||||||
use meilisearch_core::pest_error::LineColLocation::*;
|
ResponseError { inner: Box::new(err) }
|
||||||
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)
|
|
||||||
},
|
|
||||||
meilisearch_core::Error::FacetError(e) => ResponseError::FacetExpression(e.to_string()),
|
|
||||||
_ => ResponseError::Internal(err.to_string()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<meilisearch_schema::Error> for ResponseError {
|
impl From<meilisearch_schema::Error> for ResponseError {
|
||||||
fn from(err: meilisearch_schema::Error) -> ResponseError {
|
fn from(err: meilisearch_schema::Error) -> ResponseError {
|
||||||
ResponseError::Internal(err.to_string())
|
ResponseError { inner: Box::new(err) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<actix_http::Error> for ResponseError {
|
impl From<actix_http::Error> for Error {
|
||||||
fn from(err: actix_http::Error) -> ResponseError {
|
fn from(err: actix_http::Error) -> Error {
|
||||||
ResponseError::Internal(err.to_string())
|
Error::Internal(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FacetCountError> for ResponseError {
|
impl From<FacetCountError> for ResponseError {
|
||||||
fn from(other: FacetCountError) -> ResponseError {
|
fn from(err: FacetCountError) -> ResponseError {
|
||||||
ResponseError::FacetCount(other.to_string())
|
ResponseError { inner: Box::new(err) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<JsonPayloadError> for ResponseError {
|
impl From<JsonPayloadError> for Error {
|
||||||
fn from(err: JsonPayloadError) -> ResponseError {
|
fn from(err: JsonPayloadError) -> Error {
|
||||||
match err {
|
match err {
|
||||||
JsonPayloadError::Deserialize(err) => ResponseError::BadRequest(format!("Invalid JSON: {}", err)),
|
JsonPayloadError::Deserialize(err) => Error::BadRequest(format!("Invalid JSON: {}", err)),
|
||||||
JsonPayloadError::Overflow => ResponseError::PayloadTooLarge,
|
JsonPayloadError::Overflow => Error::PayloadTooLarge,
|
||||||
JsonPayloadError::ContentType => ResponseError::UnsupportedMediaType,
|
JsonPayloadError::ContentType => Error::UnsupportedMediaType,
|
||||||
JsonPayloadError::Payload(err) => ResponseError::BadRequest(format!("Problem while decoding the request: {}", err)),
|
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) -> ResponseError {
|
||||||
err.into()
|
let error = Error::from(err);
|
||||||
|
error.into()
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ use std::rc::Rc;
|
|||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use actix_service::{Service, Transform};
|
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 futures::future::{err, ok, Future, Ready};
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::error::{Error, ResponseError};
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -19,13 +19,13 @@ pub enum Authentication {
|
|||||||
|
|
||||||
impl<S: 'static, B> Transform<S> for Authentication
|
impl<S: 'static, B> Transform<S> for Authentication
|
||||||
where
|
where
|
||||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
B: 'static,
|
B: 'static,
|
||||||
{
|
{
|
||||||
type Request = ServiceRequest;
|
type Request = ServiceRequest;
|
||||||
type Response = ServiceResponse<B>;
|
type Response = ServiceResponse<B>;
|
||||||
type Error = Error;
|
type Error = actix_web::Error;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Transform = LoggingMiddleware<S>;
|
type Transform = LoggingMiddleware<S>;
|
||||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||||
@ -45,13 +45,13 @@ pub struct LoggingMiddleware<S> {
|
|||||||
|
|
||||||
impl<S, B> Service for LoggingMiddleware<S>
|
impl<S, B> Service for LoggingMiddleware<S>
|
||||||
where
|
where
|
||||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error> + 'static,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
B: 'static,
|
B: 'static,
|
||||||
{
|
{
|
||||||
type Request = ServiceRequest;
|
type Request = ServiceRequest;
|
||||||
type Response = ServiceResponse<B>;
|
type Response = ServiceResponse<B>;
|
||||||
type Error = Error;
|
type Error = actix_web::Error;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||||
@ -71,10 +71,10 @@ where
|
|||||||
let auth_header = match req.headers().get("X-Meili-API-Key") {
|
let auth_header = match req.headers().get("X-Meili-API-Key") {
|
||||||
Some(auth) => match auth.to_str() {
|
Some(auth) => match auth.to_str() {
|
||||||
Ok(auth) => auth,
|
Ok(auth) => auth,
|
||||||
Err(_) => return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())),
|
Err(_) => return Box::pin(err(ResponseError::from(Error::MissingAuthorizationHeader).into())),
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
return Box::pin(err(ResponseError::MissingAuthorizationHeader.into()));
|
return Box::pin(err(ResponseError::from(Error::MissingAuthorizationHeader).into()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ where
|
|||||||
Box::pin(svc.call(req))
|
Box::pin(svc.call(req))
|
||||||
} else {
|
} else {
|
||||||
Box::pin(err(
|
Box::pin(err(
|
||||||
ResponseError::InvalidToken(auth_header.to_string()).into()
|
ResponseError::from(Error::InvalidToken(auth_header.to_string())).into()
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,11 @@ use std::time::Instant;
|
|||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use log::error;
|
use log::error;
|
||||||
use meilisearch_core::Filter;
|
use meilisearch_core::{Filter, MainReader};
|
||||||
use meilisearch_core::facets::FacetFilter;
|
use meilisearch_core::facets::FacetFilter;
|
||||||
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, RankedMap};
|
||||||
use meilisearch_schema::{FieldId, Schema};
|
use meilisearch_schema::{FieldId, Schema};
|
||||||
use meilisearch_tokenizer::is_cjk;
|
use meilisearch_tokenizer::is_cjk;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -17,7 +17,7 @@ use serde_json::Value;
|
|||||||
use siphasher::sip::SipHasher;
|
use siphasher::sip::SipHasher;
|
||||||
use slice_group_by::GroupBy;
|
use slice_group_by::GroupBy;
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::error::{Error, ResponseError};
|
||||||
|
|
||||||
pub trait IndexSearchExt {
|
pub trait IndexSearchExt {
|
||||||
fn new_search(&self, query: String) -> SearchBuilder;
|
fn new_search(&self, query: String) -> SearchBuilder;
|
||||||
@ -107,12 +107,12 @@ impl<'a> SearchBuilder<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search(self, reader: &heed::RoTxn<MainT>) -> Result<SearchResult, ResponseError> {
|
pub fn search(self, reader: &MainReader) -> Result<SearchResult, ResponseError> {
|
||||||
let schema = self
|
let schema = self
|
||||||
.index
|
.index
|
||||||
.main
|
.main
|
||||||
.schema(reader)?
|
.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();
|
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 start = Instant::now();
|
||||||
let result = query_builder.query(reader, &self.query, self.offset..(self.offset + self.limit));
|
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 time_ms = start.elapsed().as_millis() as usize;
|
||||||
|
|
||||||
let mut all_attributes: HashSet<&str> = HashSet::new();
|
let mut all_attributes: HashSet<&str> = HashSet::new();
|
||||||
@ -194,8 +194,8 @@ impl<'a> SearchBuilder<'a> {
|
|||||||
let mut document: IndexMap<String, Value> = self
|
let mut document: IndexMap<String, Value> = self
|
||||||
.index
|
.index
|
||||||
.document(reader, Some(&all_attributes), doc.id)
|
.document(reader, Some(&all_attributes), doc.id)
|
||||||
.map_err(|e| ResponseError::retrieve_document(doc.id.0, e))?
|
.map_err(|e| Error::retrieve_document(doc.id.0, e))?
|
||||||
.ok_or(ResponseError::internal(
|
.ok_or(Error::internal(
|
||||||
"Impossible to retrieve the document; Corrupted data",
|
"Impossible to retrieve the document; Corrupted data",
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ impl<'a> SearchBuilder<'a> {
|
|||||||
|
|
||||||
pub fn get_criteria(
|
pub fn get_criteria(
|
||||||
&self,
|
&self,
|
||||||
reader: &heed::RoTxn<MainT>,
|
reader: &MainReader,
|
||||||
ranked_map: &'a RankedMap,
|
ranked_map: &'a RankedMap,
|
||||||
schema: &Schema,
|
schema: &Schema,
|
||||||
) -> Result<Option<Criteria<'a>>, ResponseError> {
|
) -> Result<Option<Criteria<'a>>, ResponseError> {
|
||||||
|
@ -7,14 +7,17 @@ pub mod models;
|
|||||||
pub mod option;
|
pub mod option;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
||||||
pub use self::data::Data;
|
|
||||||
use self::error::json_error_handler;
|
|
||||||
use actix_http::Error;
|
use actix_http::Error;
|
||||||
use actix_service::ServiceFactory;
|
use actix_service::ServiceFactory;
|
||||||
use actix_web::{dev, web, App};
|
use actix_web::{dev, web, App};
|
||||||
|
use chrono::Utc;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
||||||
use meilisearch_core::ProcessedUpdateResult;
|
use meilisearch_core::ProcessedUpdateResult;
|
||||||
|
|
||||||
|
pub use self::data::Data;
|
||||||
|
use self::error::{json_error_handler, ResponseError};
|
||||||
|
|
||||||
pub fn create_app(
|
pub fn create_app(
|
||||||
data: &Data,
|
data: &Data,
|
||||||
) -> App<
|
) -> 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) {
|
if let Some(index) = data.db.open_index(&index_uid) {
|
||||||
let db = &data.db;
|
let db = &data.db;
|
||||||
let mut writer = match db.main_write_txn() {
|
let res = db.main_write::<_, _, ResponseError>(|mut writer| {
|
||||||
Ok(writer) => writer,
|
if let Err(e) = data.db.compute_stats(&mut writer, &index_uid) {
|
||||||
Err(e) => {
|
error!("Impossible to compute stats; {}", e)
|
||||||
error!("Impossible to get write_txn; {}", e);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = data.compute_stats(&mut writer, &index_uid) {
|
if let Err(e) = data.db.set_last_update(&mut writer, &Utc::now()) {
|
||||||
error!("Impossible to compute stats; {}", e)
|
error!("Impossible to update last_update; {}", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = data.set_last_update(&mut writer) {
|
if let Err(e) = index.main.put_updated_at(&mut writer) {
|
||||||
error!("Impossible to update last_update; {}", e)
|
error!("Impossible to update updated_at; {}", e)
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
if let Err(e) = index.main.put_updated_at(&mut writer) {
|
});
|
||||||
error!("Impossible to update updated_at; {}", e)
|
match res {
|
||||||
}
|
Ok(_) => (),
|
||||||
|
Err(e) => error!("{}", e),
|
||||||
if let Err(e) = writer.commit() {
|
|
||||||
error!("Impossible to get write_txn; {}", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ use meilisearch_core::update;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::Data;
|
||||||
|
use crate::error::{Error, ResponseError};
|
||||||
use crate::helpers::Authentication;
|
use crate::helpers::Authentication;
|
||||||
use crate::routes::{IndexParam, IndexUpdateResponse};
|
use crate::routes::{IndexParam, IndexUpdateResponse};
|
||||||
use crate::Data;
|
|
||||||
|
|
||||||
type Document = IndexMap<String, Value>;
|
type Document = IndexMap<String, Value>;
|
||||||
|
|
||||||
@ -41,18 +41,19 @@ async fn get_document(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let internal_id = index.main
|
let internal_id = index.main
|
||||||
.external_to_internal_docid(&reader, &path.document_id)?
|
.external_to_internal_docid(&reader, &path.document_id)?
|
||||||
.ok_or(ResponseError::document_not_found(&path.document_id))?;
|
.ok_or(Error::document_not_found(&path.document_id))?;
|
||||||
|
|
||||||
let response: Document = index
|
let document: Document = index
|
||||||
.document(&reader, None, internal_id)?
|
.document(&reader, None, internal_id)?
|
||||||
.ok_or(ResponseError::document_not_found(&path.document_id))?;
|
.ok_or(Error::document_not_found(&path.document_id))?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(response))
|
Ok(HttpResponse::Ok().json(document))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete(
|
#[delete(
|
||||||
@ -66,16 +67,12 @@ async fn delete_document(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 update_writer = data.db.update_write_txn()?;
|
|
||||||
|
|
||||||
let mut documents_deletion = index.documents_deletion();
|
let mut documents_deletion = index.documents_deletion();
|
||||||
documents_deletion.delete_document_by_external_docid(path.document_id.clone());
|
documents_deletion.delete_document_by_external_docid(path.document_id.clone());
|
||||||
|
|
||||||
let update_id = documents_deletion.finalize(&mut update_writer)?;
|
let update_id = data.db.update_write(|w| documents_deletion.finalize(w))?;
|
||||||
|
|
||||||
update_writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -97,13 +94,12 @@ async fn get_all_documents(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 offset = params.offset.unwrap_or(0);
|
||||||
let limit = params.limit.unwrap_or(20);
|
let limit = params.limit.unwrap_or(20);
|
||||||
|
|
||||||
let reader = data.db.main_read_txn()?;
|
let reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let documents_ids: Result<BTreeSet<_>, _> = index
|
let documents_ids: Result<BTreeSet<_>, _> = index
|
||||||
.documents_fields_counts
|
.documents_fields_counts
|
||||||
.documents_ids(&reader)?
|
.documents_ids(&reader)?
|
||||||
@ -111,23 +107,21 @@ async fn get_all_documents(
|
|||||||
.take(limit)
|
.take(limit)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let documents_ids = documents_ids?;
|
|
||||||
|
|
||||||
let attributes: Option<HashSet<&str>> = params
|
let attributes: Option<HashSet<&str>> = params
|
||||||
.attributes_to_retrieve
|
.attributes_to_retrieve
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|a| a.split(',').collect());
|
.map(|a| a.split(',').collect());
|
||||||
|
|
||||||
let mut response = Vec::new();
|
let mut documents = Vec::new();
|
||||||
for document_id in documents_ids {
|
for document_id in documents_ids? {
|
||||||
if let Ok(Some(document)) =
|
if let Ok(Some(document)) =
|
||||||
index.document::<Document>(&reader, attributes.as_ref(), document_id)
|
index.document::<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<String, Value>) -> Option<String> {
|
fn find_primary_key(document: &IndexMap<String, Value>) -> Option<String> {
|
||||||
@ -155,14 +149,14 @@ async fn update_multiple_documents(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let mut schema = index
|
let mut schema = index
|
||||||
.main
|
.main
|
||||||
.schema(&reader)?
|
.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() {
|
if schema.primary_key().is_none() {
|
||||||
let id = match ¶ms.primary_key {
|
let id = match ¶ms.primary_key {
|
||||||
@ -170,16 +164,14 @@ async fn update_multiple_documents(
|
|||||||
None => body
|
None => body
|
||||||
.first()
|
.first()
|
||||||
.and_then(find_primary_key)
|
.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
|
schema
|
||||||
.set_primary_key(&id)
|
.set_primary_key(&id)
|
||||||
.map_err(ResponseError::bad_request)?;
|
.map_err(Error::bad_request)?;
|
||||||
index.main.put_schema(&mut writer, &schema)?;
|
|
||||||
writer.commit()?;
|
data.db.main_write(|w| index.main.put_schema(w, &schema))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut document_addition = if is_partial {
|
let mut document_addition = if is_partial {
|
||||||
@ -192,9 +184,7 @@ async fn update_multiple_documents(
|
|||||||
document_addition.update_document(document);
|
document_addition.update_document(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut update_writer = data.db.update_write_txn()?;
|
let update_id = data.db.update_write(|w| document_addition.finalize(w))?;
|
||||||
let update_id = document_addition.finalize(&mut update_writer)?;
|
|
||||||
update_writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -231,9 +221,8 @@ async fn delete_documents(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 mut documents_deletion = index.documents_deletion();
|
let mut documents_deletion = index.documents_deletion();
|
||||||
|
|
||||||
@ -242,9 +231,7 @@ async fn delete_documents(
|
|||||||
documents_deletion.delete_document_by_external_docid(document_id);
|
documents_deletion.delete_document_by_external_docid(document_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let update_id = documents_deletion.finalize(&mut writer)?;
|
let update_id = data.db.update_write(|w| documents_deletion.finalize(w))?;
|
||||||
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -257,13 +244,9 @@ async fn clear_all_documents(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 update_id = data.db.update_write(|w| index.clear_all(w))?;
|
||||||
|
|
||||||
let update_id = index.clear_all(&mut writer)?;
|
|
||||||
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use actix_web_macros::{get, put};
|
use actix_web_macros::{get, put};
|
||||||
use heed::types::{Str, Unit};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::error::{Error, ResponseError};
|
||||||
use crate::helpers::Authentication;
|
use crate::helpers::Authentication;
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
|
|
||||||
const UNHEALTHY_KEY: &str = "_is_unhealthy";
|
|
||||||
|
|
||||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||||
cfg.service(get_health).service(change_healthyness);
|
cfg.service(get_health).service(change_healthyness);
|
||||||
}
|
}
|
||||||
@ -16,31 +13,19 @@ pub fn services(cfg: &mut web::ServiceConfig) {
|
|||||||
#[get("/health", wrap = "Authentication::Private")]
|
#[get("/health", wrap = "Authentication::Private")]
|
||||||
async fn get_health(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
async fn get_health(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
||||||
let reader = data.db.main_read_txn()?;
|
let reader = data.db.main_read_txn()?;
|
||||||
|
if let Ok(Some(_)) = data.db.get_health(&reader) {
|
||||||
let common_store = data.db.common_store();
|
return Err(Error::Maintenance.into());
|
||||||
|
|
||||||
if let Ok(Some(_)) = common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY) {
|
|
||||||
return Err(ResponseError::Maintenance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_healthy(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
async fn set_healthy(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
||||||
let mut writer = data.db.main_write_txn()?;
|
data.db.main_write(|w| data.db.set_healthy(w))?;
|
||||||
let common_store = data.db.common_store();
|
|
||||||
common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_unhealthy(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
async fn set_unhealthy(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
||||||
let mut writer = data.db.main_write_txn()?;
|
data.db.main_write(|w| data.db.set_unhealthy(w))?;
|
||||||
let common_store = data.db.common_store();
|
|
||||||
common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &())?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use log::error;
|
|||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::error::{Error, ResponseError};
|
||||||
use crate::helpers::Authentication;
|
use crate::helpers::Authentication;
|
||||||
use crate::routes::IndexParam;
|
use crate::routes::IndexParam;
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
@ -42,28 +42,27 @@ struct IndexResponse {
|
|||||||
#[get("/indexes", wrap = "Authentication::Private")]
|
#[get("/indexes", wrap = "Authentication::Private")]
|
||||||
async fn list_indexes(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
async fn list_indexes(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
|
||||||
let reader = data.db.main_read_txn()?;
|
let reader = data.db.main_read_txn()?;
|
||||||
|
let mut indexes = Vec::new();
|
||||||
let mut response = Vec::new();
|
|
||||||
|
|
||||||
for index_uid in data.db.indexes_uids() {
|
for index_uid in data.db.indexes_uids() {
|
||||||
let index = data.db.open_index(&index_uid);
|
let index = data.db.open_index(&index_uid);
|
||||||
|
|
||||||
match index {
|
match index {
|
||||||
Some(index) => {
|
Some(index) => {
|
||||||
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",
|
"Impossible to get the name of an index",
|
||||||
))?;
|
))?;
|
||||||
let created_at = index
|
let created_at = index
|
||||||
.main
|
.main
|
||||||
.created_at(&reader)?
|
.created_at(&reader)?
|
||||||
.ok_or(ResponseError::internal(
|
.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
|
let updated_at = index
|
||||||
.main
|
.main
|
||||||
.updated_at(&reader)?
|
.updated_at(&reader)?
|
||||||
.ok_or(ResponseError::internal(
|
.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) {
|
let primary_key = match index.main.schema(&reader) {
|
||||||
@ -81,7 +80,7 @@ async fn list_indexes(data: web::Data<Data>) -> Result<HttpResponse, ResponseErr
|
|||||||
updated_at,
|
updated_at,
|
||||||
primary_key,
|
primary_key,
|
||||||
};
|
};
|
||||||
response.push(index_response);
|
indexes.push(index_response);
|
||||||
}
|
}
|
||||||
None => error!(
|
None => error!(
|
||||||
"Index {} is referenced in the indexes list but cannot be found",
|
"Index {} is referenced in the indexes list but cannot be found",
|
||||||
@ -90,7 +89,7 @@ async fn list_indexes(data: web::Data<Data>) -> Result<HttpResponse, ResponseErr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(response))
|
Ok(HttpResponse::Ok().json(indexes))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/indexes/{index_uid}", wrap = "Authentication::Private")]
|
#[get("/indexes/{index_uid}", wrap = "Authentication::Private")]
|
||||||
@ -101,24 +100,23 @@ async fn get_index(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
let name = index.main.name(&reader)?.ok_or(Error::internal(
|
||||||
let name = index.main.name(&reader)?.ok_or(ResponseError::internal(
|
"Impossible to get the name of an index",
|
||||||
"Impossible to get the name of an index",
|
|
||||||
))?;
|
))?;
|
||||||
let created_at = index
|
let created_at = index
|
||||||
.main
|
.main
|
||||||
.created_at(&reader)?
|
.created_at(&reader)?
|
||||||
.ok_or(ResponseError::internal(
|
.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
|
let updated_at = index
|
||||||
.main
|
.main
|
||||||
.updated_at(&reader)?
|
.updated_at(&reader)?
|
||||||
.ok_or(ResponseError::internal(
|
.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) {
|
let primary_key = match index.main.schema(&reader) {
|
||||||
@ -128,14 +126,15 @@ async fn get_index(
|
|||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
let index_response = IndexResponse {
|
||||||
Ok(HttpResponse::Ok().json(IndexResponse {
|
|
||||||
name,
|
name,
|
||||||
uid: path.index_uid.clone(),
|
uid: path.index_uid.clone(),
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at,
|
||||||
primary_key,
|
primary_key,
|
||||||
}))
|
};
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(index_response))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@ -152,9 +151,9 @@ async fn create_index(
|
|||||||
body: web::Json<IndexCreateRequest>,
|
body: web::Json<IndexCreateRequest>,
|
||||||
) -> Result<HttpResponse, ResponseError> {
|
) -> Result<HttpResponse, ResponseError> {
|
||||||
if let (None, None) = (body.name.clone(), body.uid.clone()) {
|
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",
|
"Index creation must have an uid",
|
||||||
));
|
).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let uid = match &body.uid {
|
let uid = match &body.uid {
|
||||||
@ -165,7 +164,7 @@ async fn create_index(
|
|||||||
{
|
{
|
||||||
uid.to_owned()
|
uid.to_owned()
|
||||||
} else {
|
} else {
|
||||||
return Err(ResponseError::InvalidIndexUid);
|
return Err(Error::InvalidIndexUid.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => loop {
|
None => loop {
|
||||||
@ -179,41 +178,41 @@ async fn create_index(
|
|||||||
let created_index = data
|
let created_index = data
|
||||||
.db
|
.db
|
||||||
.create_index(&uid)
|
.create_index(&uid)
|
||||||
.map_err(ResponseError::create_index)?;
|
.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);
|
let created_at = created_index
|
||||||
created_index.main.put_name(&mut writer, name)?;
|
.main
|
||||||
|
.created_at(&writer)?
|
||||||
|
.ok_or(Error::internal("Impossible to read created at"))?;
|
||||||
|
|
||||||
let created_at = created_index
|
let updated_at = created_index
|
||||||
.main
|
.main
|
||||||
.created_at(&writer)?
|
.updated_at(&writer)?
|
||||||
.ok_or(ResponseError::internal("Impossible to read created at"))?;
|
.ok_or(Error::internal("Impossible to read updated at"))?;
|
||||||
|
|
||||||
let updated_at = created_index
|
if let Some(id) = body.primary_key.clone() {
|
||||||
.main
|
if let Some(mut schema) = created_index.main.schema(&writer)? {
|
||||||
.updated_at(&writer)?
|
schema
|
||||||
.ok_or(ResponseError::internal("Impossible to read updated at"))?;
|
.set_primary_key(&id)
|
||||||
|
.map_err(Error::bad_request)?;
|
||||||
if let Some(id) = body.primary_key.clone() {
|
created_index.main.put_schema(&mut writer, &schema)?;
|
||||||
if let Some(mut schema) = created_index.main.schema(&writer)? {
|
}
|
||||||
schema
|
|
||||||
.set_primary_key(&id)
|
|
||||||
.map_err(ResponseError::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(index_response))
|
||||||
|
|
||||||
Ok(HttpResponse::Created().json(IndexResponse {
|
|
||||||
name: name.to_string(),
|
|
||||||
uid,
|
|
||||||
created_at,
|
|
||||||
updated_at,
|
|
||||||
primary_key: body.primary_key.clone(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@ -242,49 +241,47 @@ async fn update_index(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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()?;
|
data.db.main_write::<_, _, ResponseError>(|writer| {
|
||||||
|
if let Some(name) = &body.name {
|
||||||
|
index.main.put_name(writer, name)?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(name) = &body.name {
|
if let Some(id) = body.primary_key.clone() {
|
||||||
index.main.put_name(&mut writer, name)?;
|
if let Some(mut schema) = index.main.schema(writer)? {
|
||||||
}
|
match schema.primary_key() {
|
||||||
|
Some(_) => {
|
||||||
if let Some(id) = body.primary_key.clone() {
|
return Err(Error::bad_request(
|
||||||
if let Some(mut schema) = index.main.schema(&writer)? {
|
"The primary key cannot be updated",
|
||||||
match schema.primary_key() {
|
).into());
|
||||||
Some(_) => {
|
}
|
||||||
return Err(ResponseError::bad_request(
|
None => {
|
||||||
"The primary key cannot be updated",
|
schema.set_primary_key(&id)?;
|
||||||
));
|
index.main.put_schema(writer, &schema)?;
|
||||||
}
|
}
|
||||||
None => {
|
|
||||||
schema.set_primary_key(&id)?;
|
|
||||||
index.main.put_schema(&mut writer, &schema)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
index.main.put_updated_at(writer)?;
|
||||||
|
Ok(())
|
||||||
index.main.put_updated_at(&mut writer)?;
|
})?;
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
let reader = data.db.main_read_txn()?;
|
let reader = data.db.main_read_txn()?;
|
||||||
|
let name = index.main.name(&reader)?.ok_or(Error::internal(
|
||||||
let name = index.main.name(&reader)?.ok_or(ResponseError::internal(
|
"Impossible to get the name of an index",
|
||||||
"Impossible to get the name of an index",
|
|
||||||
))?;
|
))?;
|
||||||
let created_at = index
|
let created_at = index
|
||||||
.main
|
.main
|
||||||
.created_at(&reader)?
|
.created_at(&reader)?
|
||||||
.ok_or(ResponseError::internal(
|
.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
|
let updated_at = index
|
||||||
.main
|
.main
|
||||||
.updated_at(&reader)?
|
.updated_at(&reader)?
|
||||||
.ok_or(ResponseError::internal(
|
.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) {
|
let primary_key = match index.main.schema(&reader) {
|
||||||
@ -295,13 +292,15 @@ async fn update_index(
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(IndexResponse {
|
let index_response = IndexResponse {
|
||||||
name,
|
name,
|
||||||
uid: path.index_uid.clone(),
|
uid: path.index_uid.clone(),
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at,
|
||||||
primary_key,
|
primary_key,
|
||||||
}))
|
};
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(index_response))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/indexes/{index_uid}", wrap = "Authentication::Private")]
|
#[delete("/indexes/{index_uid}", wrap = "Authentication::Private")]
|
||||||
@ -331,7 +330,7 @@ async fn get_update_status(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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()?;
|
let reader = data.db.update_read_txn()?;
|
||||||
|
|
||||||
@ -339,10 +338,10 @@ async fn get_update_status(
|
|||||||
|
|
||||||
match status {
|
match status {
|
||||||
Some(status) => Ok(HttpResponse::Ok().json(status)),
|
Some(status) => Ok(HttpResponse::Ok().json(status)),
|
||||||
None => Err(ResponseError::NotFound(format!(
|
None => Err(Error::NotFound(format!(
|
||||||
"Update {} not found",
|
"Update {} not found",
|
||||||
path.update_id
|
path.update_id
|
||||||
))),
|
)).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,7 +353,7 @@ async fn get_all_updates_status(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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()?;
|
let reader = data.db.update_read_txn()?;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use actix_web_macros::get;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::error::{ResponseError, FacetCountError};
|
use crate::error::{Error, FacetCountError, ResponseError};
|
||||||
use crate::helpers::meilisearch::IndexSearchExt;
|
use crate::helpers::meilisearch::IndexSearchExt;
|
||||||
use crate::helpers::Authentication;
|
use crate::helpers::Authentication;
|
||||||
use crate::routes::IndexParam;
|
use crate::routes::IndexParam;
|
||||||
@ -45,14 +45,13 @@ async fn search_with_url_query(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let schema = index
|
let schema = index
|
||||||
.main
|
.main
|
||||||
.schema(&reader)?
|
.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());
|
let mut search_builder = index.new_search(params.q.clone());
|
||||||
|
|
||||||
@ -88,9 +87,9 @@ async fn search_with_url_query(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref facet_filters) = params.facet_filters {
|
if let Some(ref facet_filters) = params.facet_filters {
|
||||||
match index.main.attributes_for_faceting(&reader)? {
|
let attrs = index.main.attributes_for_faceting(&reader)?;
|
||||||
Some(ref attrs) => { search_builder.add_facet_filters(FacetFilter::from_str(facet_filters, &schema, attrs)?); },
|
if let Some(attrs) = attrs {
|
||||||
None => return Err(ResponseError::FacetExpression("can't filter on facets, as no facet is set".to_string()))
|
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)?;
|
let field_ids = prepare_facet_list(&facets, &schema, attrs)?;
|
||||||
search_builder.add_facets(field_ids);
|
search_builder.add_facets(field_ids);
|
||||||
},
|
},
|
||||||
None => return Err(FacetCountError::NoFacetSet.into())
|
None => return Err(FacetCountError::NoFacetSet.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,8 +159,9 @@ async fn search_with_url_query(
|
|||||||
search_builder.get_matches();
|
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
|
/// Parses the incoming string into an array of attributes for which to return a count. It returns
|
||||||
|
@ -3,7 +3,7 @@ use actix_web_macros::{delete, get, post};
|
|||||||
use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES};
|
use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES};
|
||||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::error::{Error, ResponseError};
|
||||||
use crate::helpers::Authentication;
|
use crate::helpers::Authentication;
|
||||||
use crate::routes::{IndexParam, IndexUpdateResponse};
|
use crate::routes::{IndexParam, IndexUpdateResponse};
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
@ -37,15 +37,16 @@ async fn update_all(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 update_id = data.db.update_write::<_, _, ResponseError>(|writer| {
|
||||||
let settings = body
|
let settings = body
|
||||||
.into_inner()
|
.into_inner()
|
||||||
.into_update()
|
.into_update()
|
||||||
.map_err(ResponseError::bad_request)?;
|
.map_err(Error::bad_request)?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
let update_id = index.settings_update(writer, settings)?;
|
||||||
writer.commit()?;
|
Ok(update_id)
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -58,22 +59,22 @@ async fn get_all(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let stop_words_fst = index.main.stop_words_fst(&reader)?;
|
let stop_words: BTreeSet<String> = index
|
||||||
let stop_words = stop_words_fst.stream().into_strs()?;
|
.main
|
||||||
let stop_words: BTreeSet<String> = stop_words.into_iter().collect();
|
.stop_words(&reader)?
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
let synonyms_fst = index.main.synonyms_fst(&reader)?;
|
let synonyms_list = index.main.synonyms(&reader)?;
|
||||||
let synonyms_list = synonyms_fst.stream().into_strs()?;
|
|
||||||
|
|
||||||
let mut synonyms = BTreeMap::new();
|
let mut synonyms = BTreeMap::new();
|
||||||
let index_synonyms = &index.synonyms;
|
let index_synonyms = &index.synonyms;
|
||||||
for synonym in synonyms_list {
|
for synonym in synonyms_list {
|
||||||
let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?;
|
let list = index_synonyms.synonyms(&reader, synonym.as_bytes())?;
|
||||||
let list = alternative_list.stream().into_strs()?;
|
|
||||||
synonyms.insert(synonym, list);
|
synonyms.insert(synonym, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,8 +139,7 @@ async fn delete_all(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
ranking_rules: UpdateState::Clear,
|
ranking_rules: UpdateState::Clear,
|
||||||
@ -153,8 +153,7 @@ async fn delete_all(
|
|||||||
attributes_for_faceting: UpdateState::Clear,
|
attributes_for_faceting: UpdateState::Clear,
|
||||||
};
|
};
|
||||||
|
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -170,7 +169,7 @@ async fn get_rules(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let ranking_rules = index
|
let ranking_rules = index
|
||||||
@ -196,17 +195,15 @@ async fn update_rules(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = Settings {
|
||||||
ranking_rules: Some(body.into_inner()),
|
ranking_rules: Some(body.into_inner()),
|
||||||
..Settings::default()
|
..Settings::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let settings = settings.into_update().map_err(Error::bad_request)?;
|
||||||
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -222,17 +219,14 @@ async fn delete_rules(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
ranking_rules: UpdateState::Clear,
|
ranking_rules: UpdateState::Clear,
|
||||||
..SettingsUpdate::default()
|
..SettingsUpdate::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -248,7 +242,7 @@ async fn get_distinct(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
let distinct_attribute = index.main.distinct_attribute(&reader)?;
|
let distinct_attribute = index.main.distinct_attribute(&reader)?;
|
||||||
|
|
||||||
@ -267,17 +261,15 @@ async fn update_distinct(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = Settings {
|
||||||
distinct_attribute: Some(body.into_inner()),
|
distinct_attribute: Some(body.into_inner()),
|
||||||
..Settings::default()
|
..Settings::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let settings = settings.into_update().map_err(Error::bad_request)?;
|
||||||
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -293,17 +285,14 @@ async fn delete_distinct(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
distinct_attribute: UpdateState::Clear,
|
distinct_attribute: UpdateState::Clear,
|
||||||
..SettingsUpdate::default()
|
..SettingsUpdate::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -319,7 +308,7 @@ async fn get_searchable(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
let schema = index.main.schema(&reader)?;
|
let schema = index.main.schema(&reader)?;
|
||||||
let searchable_attributes: Option<Vec<String>> =
|
let searchable_attributes: Option<Vec<String>> =
|
||||||
@ -340,17 +329,16 @@ async fn update_searchable(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = Settings {
|
||||||
searchable_attributes: Some(body.into_inner()),
|
searchable_attributes: Some(body.into_inner()),
|
||||||
..Settings::default()
|
..Settings::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let settings = settings.into_update().map_err(Error::bad_request)?;
|
||||||
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
|
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -366,16 +354,14 @@ async fn delete_searchable(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
searchable_attributes: UpdateState::Clear,
|
searchable_attributes: UpdateState::Clear,
|
||||||
..SettingsUpdate::default()
|
..SettingsUpdate::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -391,7 +377,7 @@ async fn get_displayed(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let schema = index.main.schema(&reader)?;
|
let schema = index.main.schema(&reader)?;
|
||||||
@ -414,17 +400,15 @@ async fn update_displayed(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = Settings {
|
||||||
displayed_attributes: Some(body.into_inner()),
|
displayed_attributes: Some(body.into_inner()),
|
||||||
..Settings::default()
|
..Settings::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let settings = settings.into_update().map_err(Error::bad_request)?;
|
||||||
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -440,16 +424,14 @@ async fn delete_displayed(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
displayed_attributes: UpdateState::Clear,
|
displayed_attributes: UpdateState::Clear,
|
||||||
..SettingsUpdate::default()
|
..SettingsUpdate::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -465,7 +447,7 @@ async fn get_accept_new_fields(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let schema = index.main.schema(&reader)?;
|
let schema = index.main.schema(&reader)?;
|
||||||
@ -487,17 +469,15 @@ async fn update_accept_new_fields(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = Settings {
|
||||||
accept_new_fields: Some(body.into_inner()),
|
accept_new_fields: Some(body.into_inner()),
|
||||||
..Settings::default()
|
..Settings::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let settings = settings.into_update().map_err(Error::bad_request)?;
|
||||||
let settings = settings.into_update().map_err(ResponseError::bad_request)?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use serde::Serialize;
|
|||||||
use sysinfo::{NetworkExt, ProcessExt, ProcessorExt, System, SystemExt};
|
use sysinfo::{NetworkExt, ProcessExt, ProcessorExt, System, SystemExt};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::error::{Error, ResponseError};
|
||||||
use crate::helpers::Authentication;
|
use crate::helpers::Authentication;
|
||||||
use crate::routes::IndexParam;
|
use crate::routes::IndexParam;
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
@ -39,7 +39,7 @@ async fn index_stats(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ async fn index_stats(
|
|||||||
let update_reader = data.db.update_read_txn()?;
|
let update_reader = data.db.update_read_txn()?;
|
||||||
|
|
||||||
let is_indexing =
|
let is_indexing =
|
||||||
data.is_indexing(&update_reader, &path.index_uid)?
|
data.db.is_indexing(&update_reader, &path.index_uid)?
|
||||||
.ok_or(ResponseError::internal(
|
.ok_or(Error::internal(
|
||||||
"Impossible to know if the database is indexing",
|
"Impossible to know if the database is indexing",
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
@ -86,8 +86,8 @@ async fn get_stats(data: web::Data<Data>) -> Result<HttpResponse, ResponseError>
|
|||||||
|
|
||||||
let fields_distribution = index.main.fields_distribution(&reader)?.unwrap_or_default();
|
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(
|
||||||
ResponseError::internal("Impossible to know if the database is indexing"),
|
Error::internal("Impossible to know if the database is indexing"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let response = IndexStatsResponse {
|
let response = IndexStatsResponse {
|
||||||
@ -111,7 +111,7 @@ async fn get_stats(data: web::Data<Data>) -> Result<HttpResponse, ResponseError>
|
|||||||
.filter(|metadata| metadata.is_file())
|
.filter(|metadata| metadata.is_file())
|
||||||
.fold(0, |acc, m| acc + m.len());
|
.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 {
|
Ok(HttpResponse::Ok().json(StatsResult {
|
||||||
database_size,
|
database_size,
|
||||||
|
@ -3,7 +3,7 @@ use actix_web_macros::{delete, get, post};
|
|||||||
use meilisearch_core::settings::{SettingsUpdate, UpdateState};
|
use meilisearch_core::settings::{SettingsUpdate, UpdateState};
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::error::{Error, ResponseError};
|
||||||
use crate::helpers::Authentication;
|
use crate::helpers::Authentication;
|
||||||
use crate::routes::{IndexParam, IndexUpdateResponse};
|
use crate::routes::{IndexParam, IndexUpdateResponse};
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
@ -23,10 +23,9 @@ async fn get(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
let stop_words_fst = index.main.stop_words_fst(&reader)?;
|
let stop_words = index.main.stop_words(&reader)?;
|
||||||
let stop_words = stop_words_fst.stream().into_strs()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(stop_words))
|
Ok(HttpResponse::Ok().json(stop_words))
|
||||||
}
|
}
|
||||||
@ -43,16 +42,14 @@ async fn update(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
stop_words: UpdateState::Update(body.into_inner()),
|
stop_words: UpdateState::Update(body.into_inner()),
|
||||||
..SettingsUpdate::default()
|
..SettingsUpdate::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -68,16 +65,14 @@ async fn delete(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
stop_words: UpdateState::Clear,
|
stop_words: UpdateState::Clear,
|
||||||
..SettingsUpdate::default()
|
..SettingsUpdate::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use actix_web_macros::{delete, get, post};
|
|||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use meilisearch_core::settings::{SettingsUpdate, UpdateState};
|
use meilisearch_core::settings::{SettingsUpdate, UpdateState};
|
||||||
|
|
||||||
use crate::error::ResponseError;
|
use crate::error::{Error, ResponseError};
|
||||||
use crate::helpers::Authentication;
|
use crate::helpers::Authentication;
|
||||||
use crate::routes::{IndexParam, IndexUpdateResponse};
|
use crate::routes::{IndexParam, IndexUpdateResponse};
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
@ -25,18 +25,16 @@ async fn get(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 reader = data.db.main_read_txn()?;
|
||||||
|
|
||||||
let synonyms_fst = index.main.synonyms_fst(&reader)?;
|
let synonyms_list = index.main.synonyms(&reader)?;
|
||||||
let synonyms_list = synonyms_fst.stream().into_strs()?;
|
|
||||||
|
|
||||||
let mut synonyms = IndexMap::new();
|
let mut synonyms = IndexMap::new();
|
||||||
let index_synonyms = &index.synonyms;
|
let index_synonyms = &index.synonyms;
|
||||||
for synonym in synonyms_list {
|
for synonym in synonyms_list {
|
||||||
let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?;
|
let list = index_synonyms.synonyms(&reader, synonym.as_bytes())?;
|
||||||
let list = alternative_list.stream().into_strs()?;
|
|
||||||
synonyms.insert(synonym, list);
|
synonyms.insert(synonym, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,16 +53,14 @@ async fn update(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
synonyms: UpdateState::Update(body.into_inner()),
|
synonyms: UpdateState::Update(body.into_inner()),
|
||||||
..SettingsUpdate::default()
|
..SettingsUpdate::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
@ -80,17 +76,14 @@ async fn delete(
|
|||||||
let index = data
|
let index = data
|
||||||
.db
|
.db
|
||||||
.open_index(&path.index_uid)
|
.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 {
|
let settings = SettingsUpdate {
|
||||||
synonyms: UpdateState::Clear,
|
synonyms: UpdateState::Clear,
|
||||||
..SettingsUpdate::default()
|
..SettingsUpdate::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut writer = data.db.update_write_txn()?;
|
let update_id = data.db.update_write(|w| index.settings_update(w, settings))?;
|
||||||
let update_id = index.settings_update(&mut writer, settings)?;
|
|
||||||
|
|
||||||
writer.commit()?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id)))
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ async fn delete() {
|
|||||||
assert_eq!(status_code, 404);
|
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]
|
#[actix_rt::test]
|
||||||
async fn delete_batch() {
|
async fn delete_batch() {
|
||||||
let mut server = common::Server::with_uid("movies");
|
let mut server = common::Server::with_uid("movies");
|
||||||
|
@ -382,7 +382,7 @@ async fn create_index_failed() {
|
|||||||
|
|
||||||
assert_eq!(status_code, 400);
|
assert_eq!(status_code, 400);
|
||||||
let message = res_value["message"].as_str().unwrap();
|
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");
|
assert_eq!(message, "Index creation must have an uid");
|
||||||
|
|
||||||
// 3 - Create a index with extra data
|
// 3 - Create a index with extra data
|
||||||
@ -462,7 +462,7 @@ async fn create_index_with_invalid_uid() {
|
|||||||
|
|
||||||
assert_eq!(status_code, 400);
|
assert_eq!(status_code, 400);
|
||||||
let message = response["message"].as_str().unwrap();
|
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 (_).");
|
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
|
// 2 - Create the index with invalid uid
|
||||||
@ -475,7 +475,7 @@ async fn create_index_with_invalid_uid() {
|
|||||||
|
|
||||||
assert_eq!(status_code, 400);
|
assert_eq!(status_code, 400);
|
||||||
let message = response["message"].as_str().unwrap();
|
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 (_).");
|
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
|
// 3 - Create the index with invalid uid
|
||||||
@ -488,7 +488,7 @@ async fn create_index_with_invalid_uid() {
|
|||||||
|
|
||||||
assert_eq!(status_code, 400);
|
assert_eq!(status_code, 400);
|
||||||
let message = response["message"].as_str().unwrap();
|
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 (_).");
|
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
|
// 4 - Create the index with invalid uid
|
||||||
@ -501,7 +501,7 @@ async fn create_index_with_invalid_uid() {
|
|||||||
|
|
||||||
assert_eq!(status_code, 400);
|
assert_eq!(status_code, 400);
|
||||||
let message = response["message"].as_str().unwrap();
|
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 (_).");
|
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 (response, status_code) = server.add_or_replace_multiple_documents_sync(body).await;
|
||||||
|
|
||||||
let expected = json!({
|
let message = response["message"].as_str().unwrap();
|
||||||
"message": "Could not infer a primary key"
|
assert_eq!(response.as_object().unwrap().len(), 4);
|
||||||
});
|
assert_eq!(message, "Could not infer a primary key");
|
||||||
|
|
||||||
assert_eq!(status_code, 400);
|
assert_eq!(status_code, 400);
|
||||||
assert_json_eq!(response, expected, ordered: false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -8,6 +8,7 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = "1.2.1"
|
bincode = "1.2.1"
|
||||||
indexmap = { version = "1.3.2", features = ["serde-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 = { version = "1.0.105", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.50", features = ["preserve_order"] }
|
serde_json = { version = "1.0.50", features = ["preserve_order"] }
|
||||||
toml = { version = "0.5.6", features = ["preserve_order"] }
|
toml = { version = "0.5.6", features = ["preserve_order"] }
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
use std::{error, fmt};
|
use std::{error, fmt};
|
||||||
|
|
||||||
|
use meilisearch_error::{ErrorCode, Code};
|
||||||
|
|
||||||
pub type SResult<T> = Result<T, Error>;
|
pub type SResult<T> = Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -22,3 +23,15 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for Error {}
|
impl error::Error for Error {}
|
||||||
|
|
||||||
|
impl ErrorCode for Error {
|
||||||
|
fn error_code(&self) -> Code {
|
||||||
|
use Error::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
FieldNameNotFound(_) => Code::Internal,
|
||||||
|
MaxFieldsLimitExceeded => Code::MaxFieldsLimitExceeded,
|
||||||
|
PrimaryKeyAlreadyPresent => Code::PrimaryKeyAlreadyPresent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user