clean code, and fix errors

This commit is contained in:
mpostma 2020-12-22 14:02:41 +01:00
parent 29b1f55bb0
commit 7c9eaaeadb
20 changed files with 3723 additions and 474 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# the milli project is a library
/target

3447
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,16 @@
[package]
name = "meilisearch-http"
authors = ["Quentin de Quelen <quentin@dequelen.me>", "Clément Renault <clement@meilisearch.com>"]
description = "MeiliSearch HTTP server"
version = "0.17.0"
license = "MIT"
authors = [
"Quentin de Quelen <quentin@dequelen.me>",
"Clément Renault <clement@meilisearch.com>",
]
edition = "2018"
license = "MIT"
name = "meilisearch-http"
version = "0.17.0"
[[bin]]
name = "meilisearch"
path = "src/main.rs"
[features]
default = ["sentry"]
[build-dependencies]
vergen = "3.1.0"
[dependencies]
actix-cors = "0.5.3"
@ -22,6 +18,7 @@ actix-http = "2"
actix-rt = "1"
actix-service = "1.0.6"
actix-web = { version = "3.3.2", features = ["rustls"] }
byte-unit = { version = "4.0.9", default-features = false, features = ["std"] }
bytes = "0.6.0"
chrono = { version = "0.4.19", features = ["serde"] }
crossbeam-channel = "0.5.0"
@ -29,19 +26,16 @@ env_logger = "0.8.2"
flate2 = "1.0.18"
futures = "0.3.7"
http = "0.2.1"
indexmap = { version = "1.3.2", features = ["serde-1"] }
indexmap = { version = "1.3.2", features = ["serde-1"] }
log = "0.4.8"
main_error = "0.1.0"
meilisearch-core = { path = "../meilisearch-core", version = "0.17.0" }
meilisearch-error = { path = "../meilisearch-error", version = "0.17.0" }
meilisearch-schema = { path = "../meilisearch-schema", version = "0.17.0" }
meilisearch-tokenizer = {path = "../meilisearch-tokenizer", version = "0.17.0"}
milli = { path = "../milli" }
mime = "0.3.16"
once_cell = "1.5.2"
rand = "0.7.3"
regex = "1.4.2"
rustls = "0.18"
serde = { version = "1.0.117", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0.59", features = ["preserve_order"] }
serde_qs = "0.8.1"
sha2 = "0.9.1"
@ -50,37 +44,27 @@ slice-group-by = "0.2.6"
structopt = "0.3.20"
tar = "0.4.29"
tempfile = "3.1.0"
tokio = { version = "0.2.18", features = ["macros"] }
ureq = { version = "1.5.1", features = ["tls"], default-features = false }
tokio = "*"
ureq = { version = "1.5.1", default-features = false, features = ["tls"] }
walkdir = "2.3.1"
whoami = "1.0.0"
meilisearch-error = { path = "../MeiliSearch/meilisearch-error" }
[dependencies.sentry]
version = "0.18.1"
default-features = false
features = [
"with_client_implementation",
"with_panic",
"with_failure",
"with_device_info",
"with_rust_info",
"with_reqwest_transport",
"with_rustls",
"with_env_logger"
]
features = ["with_client_implementation", "with_panic", "with_failure", "with_device_info", "with_rust_info", "with_reqwest_transport", "with_rustls", "with_env_logger"]
optional = true
version = "0.18.1"
[dev-dependencies]
serde_url_params = "0.2.0"
tempdir = "0.3.7"
assert-json-diff = { branch = "master", git = "https://github.com/qdequele/assert-json-diff" }
tokio = { version = "0.2.18", features = ["macros", "time"] }
[dev-dependencies.assert-json-diff]
git = "https://github.com/qdequele/assert-json-diff"
branch = "master"
[build-dependencies]
vergen = "3.1.0"
[features]
default = ["sentry"]
[target.'cfg(unix)'.dependencies]
jemallocator = "0.3.2"

View File

@ -3,12 +3,11 @@ use std::ops::Deref;
use std::path::PathBuf;
use std::sync::Arc;
use meilisearch_core::{Database, DatabaseOptions, Index};
use sha2::Digest;
use milli::Index;
use crate::error::{Error as MSError, ResponseError};
use crate::index_update_callback;
use crate::option::Opt;
use crate::updates::UpdateQueue;
#[derive(Clone)]
pub struct Data {
@ -25,7 +24,8 @@ impl Deref for Data {
#[derive(Clone)]
pub struct DataInner {
pub db: Arc<Database>,
pub indexes: Arc<Index>,
pub update_store: UpdateQueue,
pub db_path: String,
pub dumps_dir: PathBuf,
pub dump_batch_size: usize,
@ -59,104 +59,7 @@ impl ApiKeys {
}
impl Data {
pub fn new(opt: Opt) -> Result<Data, Box<dyn Error>> {
let db_path = opt.db_path.clone();
let dumps_dir = opt.dumps_dir.clone();
let dump_batch_size = opt.dump_batch_size;
let server_pid = std::process::id();
let db_opt = DatabaseOptions {
main_map_size: opt.max_mdb_size,
update_map_size: opt.max_udb_size,
};
let http_payload_size_limit = opt.http_payload_size_limit;
let db = Arc::new(Database::open_or_create(opt.db_path, db_opt)?);
let mut api_keys = ApiKeys {
master: opt.master_key,
private: None,
public: None,
};
api_keys.generate_missing_api_keys();
let inner_data = DataInner {
db: db.clone(),
db_path,
dumps_dir,
dump_batch_size,
api_keys,
server_pid,
http_payload_size_limit,
};
let data = Data {
inner: Arc::new(inner_data),
};
let callback_context = data.clone();
db.set_update_callback(Box::new(move |index_uid, status| {
index_update_callback(&index_uid, &callback_context, status);
}));
Ok(data)
}
fn create_index(&self, uid: &str) -> Result<Index, ResponseError> {
if !uid
.chars()
.all(|x| x.is_ascii_alphanumeric() || x == '-' || x == '_')
{
return Err(MSError::InvalidIndexUid.into());
}
let created_index = self.db.create_index(&uid).map_err(|e| match e {
meilisearch_core::Error::IndexAlreadyExists => e.into(),
_ => ResponseError::from(MSError::create_index(e)),
})?;
self.db.main_write::<_, _, ResponseError>(|mut writer| {
created_index.main.put_name(&mut writer, uid)?;
created_index
.main
.created_at(&writer)?
.ok_or(MSError::internal("Impossible to read created at"))?;
created_index
.main
.updated_at(&writer)?
.ok_or(MSError::internal("Impossible to read updated at"))?;
Ok(())
})?;
Ok(created_index)
}
pub fn get_or_create_index<F, R>(&self, uid: &str, f: F) -> Result<R, ResponseError>
where
F: FnOnce(&Index) -> Result<R, ResponseError>,
{
let mut index_has_been_created = false;
let index = match self.db.open_index(&uid) {
Some(index) => index,
None => {
index_has_been_created = true;
self.create_index(&uid)?
}
};
match f(&index) {
Ok(r) => Ok(r),
Err(err) => {
if index_has_been_created {
let _ = self.db.delete_index(&uid);
}
Err(err)
}
}
pub fn new(_opt: Opt) -> Result<Data, Box<dyn Error>> {
todo!()
}
}

View File

@ -8,9 +8,6 @@ use actix_web::web;
use chrono::offset::Utc;
use indexmap::IndexMap;
use log::{error, info};
use meilisearch_core::{MainWriter, MainReader, UpdateReader};
use meilisearch_core::settings::Settings;
use meilisearch_core::update::{apply_settings_update, apply_documents_addition};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use serde_json::json;
@ -20,6 +17,7 @@ use crate::Data;
use crate::error::{Error, ResponseError};
use crate::helpers::compression;
use crate::routes::index;
use crate::routes::setting::Settings;
use crate::routes::index::IndexResponse;
// Mutex to share dump progress.

View File

@ -6,9 +6,9 @@ use actix_web as aweb;
use actix_web::error::{JsonPayloadError, QueryPayloadError};
use actix_web::http::StatusCode;
use serde::ser::{Serialize, Serializer, SerializeStruct};
use meilisearch_error::{ErrorCode, Code};
#[derive(Debug)]
pub struct ResponseError {
inner: Box<dyn ErrorCode>,
@ -34,18 +34,6 @@ impl From<Error> for ResponseError {
}
}
impl From<meilisearch_core::Error> for ResponseError {
fn from(err: meilisearch_core::Error) -> ResponseError {
ResponseError { inner: Box::new(err) }
}
}
impl From<meilisearch_schema::Error> for ResponseError {
fn from(err: meilisearch_schema::Error) -> ResponseError {
ResponseError { inner: Box::new(err) }
}
}
impl From<FacetCountError> for ResponseError {
fn from(err: FacetCountError) -> ResponseError {
ResponseError { inner: Box::new(err) }
@ -123,8 +111,9 @@ impl ErrorCode for Error {
SearchDocuments(_) => Code::SearchDocuments,
PayloadTooLarge => Code::PayloadTooLarge,
UnsupportedMediaType => Code::UnsupportedMediaType,
DumpAlreadyInProgress => Code::DumpAlreadyInProgress,
DumpProcessFailed(_) => Code::DumpProcessFailed,
_ => unreachable!()
//DumpAlreadyInProgress => Code::DumpAlreadyInProgress,
//DumpProcessFailed(_) => Code::DumpProcessFailed,
}
}
}
@ -270,12 +259,6 @@ impl From<actix_http::Error> for Error {
}
}
impl From<meilisearch_core::Error> for Error {
fn from(err: meilisearch_core::Error) -> Error {
Error::Internal(err.to_string())
}
}
impl From<serde_json::error::Error> for Error {
fn from(err: serde_json::error::Error) -> Error {
Error::Internal(err.to_string())

View File

@ -3,24 +3,20 @@
pub mod data;
pub mod error;
pub mod helpers;
pub mod models;
pub mod option;
pub mod routes;
pub mod analytics;
pub mod snapshot;
pub mod dump;
mod updates;
//pub mod analytics;
//pub mod snapshot;
//pub mod dump;
use actix_http::Error;
use actix_service::ServiceFactory;
use actix_web::{dev, web, App};
use chrono::Utc;
use log::error;
use meilisearch_core::{Index, MainWriter, ProcessedUpdateResult};
pub use option::Opt;
pub use self::data::Data;
use self::error::{payload_error_handler, ResponseError};
use self::error::payload_error_handler;
pub fn create_app(
data: &Data,
@ -55,8 +51,8 @@ pub fn create_app(
.configure(routes::synonym::services)
.configure(routes::health::services)
.configure(routes::stats::services)
.configure(routes::key::services)
.configure(routes::dump::services);
.configure(routes::key::services);
//.configure(routes::dump::services);
if enable_frontend {
app
.service(routes::load_html)
@ -65,40 +61,3 @@ pub fn create_app(
app
}
}
pub fn index_update_callback_txn(index: Index, index_uid: &str, data: &Data, mut writer: &mut MainWriter) -> Result<(), String> {
if let Err(e) = data.db.compute_stats(&mut writer, index_uid) {
return Err(format!("Impossible to compute stats; {}", e));
}
if let Err(e) = data.db.set_last_update(&mut writer, &Utc::now()) {
return Err(format!("Impossible to update last_update; {}", e));
}
if let Err(e) = index.main.put_updated_at(&mut writer) {
return Err(format!("Impossible to update updated_at; {}", e));
}
Ok(())
}
pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpdateResult) {
if status.error.is_some() {
return;
}
if let Some(index) = data.db.open_index(index_uid) {
let db = &data.db;
let res = db.main_write::<_, _, ResponseError>(|mut writer| {
if let Err(e) = index_update_callback_txn(index, index_uid, data, &mut writer) {
error!("{}", e);
}
Ok(())
});
match res {
Ok(_) => (),
Err(e) => error!("{}", e),
}
}
}

View File

@ -1,14 +1,13 @@
use std::{env, thread};
use std::env;
use actix_cors::Cors;
use actix_web::{middleware, HttpServer};
use main_error::MainError;
use meilisearch_http::helpers::NormalizePath;
use meilisearch_http::{create_app, index_update_callback, Data, Opt};
use meilisearch_http::{create_app, Data, Opt};
use structopt::StructOpt;
use meilisearch_http::{snapshot, dump};
mod analytics;
//mod analytics;
#[cfg(target_os = "linux")]
#[global_allocator]
@ -52,31 +51,25 @@ async fn main() -> Result<(), MainError> {
_ => unreachable!(),
}
if let Some(path) = &opt.import_snapshot {
snapshot::load_snapshot(&opt.db_path, path, opt.ignore_snapshot_if_db_exists, opt.ignore_missing_snapshot)?;
}
//if let Some(path) = &opt.import_snapshot {
//snapshot::load_snapshot(&opt.db_path, path, opt.ignore_snapshot_if_db_exists, opt.ignore_missing_snapshot)?;
//}
let data = Data::new(opt.clone())?;
if !opt.no_analytics {
let analytics_data = data.clone();
let analytics_opt = opt.clone();
thread::spawn(move || analytics::analytics_sender(analytics_data, analytics_opt));
}
//if !opt.no_analytics {
//let analytics_data = data.clone();
//let analytics_opt = opt.clone();
//thread::spawn(move || analytics::analytics_sender(analytics_data, analytics_opt));
//}
let data_cloned = data.clone();
data.db.set_update_callback(Box::new(move |name, status| {
index_update_callback(name, &data_cloned, status);
}));
//if let Some(path) = &opt.import_dump {
//dump::import_dump(&data, path, opt.dump_batch_size)?;
//}
if let Some(path) = &opt.import_dump {
dump::import_dump(&data, path, opt.dump_batch_size)?;
}
if opt.schedule_snapshot {
snapshot::schedule_snapshot(data.clone(), &opt.snapshot_dir, opt.snapshot_interval_sec.unwrap_or(86400))?;
}
//if opt.schedule_snapshot {
//snapshot::schedule_snapshot(data.clone(), &opt.snapshot_dir, opt.snapshot_interval_sec.unwrap_or(86400))?;
//}
print_launch_resume(&opt, &data);

View File

@ -3,6 +3,7 @@ use std::io::{BufReader, Read};
use std::path::PathBuf;
use std::sync::Arc;
use byte_unit::Byte;
use rustls::internal::pemfile::{certs, pkcs8_private_keys, rsa_private_keys};
use rustls::{
AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth,
@ -12,7 +13,7 @@ use structopt::StructOpt;
const POSSIBLE_ENV: [&str; 2] = ["development", "production"];
#[derive(Debug, Default, Clone, StructOpt)]
#[derive(Debug, Clone, StructOpt)]
pub struct Opt {
/// The destination where the database must be created.
#[structopt(long, env = "MEILI_DB_PATH", default_value = "./data.ms")]
@ -49,16 +50,16 @@ pub struct Opt {
pub no_analytics: bool,
/// The maximum size, in bytes, of the main lmdb database directory
#[structopt(long, env = "MEILI_MAX_MDB_SIZE", default_value = "107374182400")] // 100GB
pub max_mdb_size: usize,
#[structopt(long, env = "MEILI_MAX_MDB_SIZE", default_value = "100 GiB")]
pub max_mdb_size: Byte,
/// The maximum size, in bytes, of the update lmdb database directory
#[structopt(long, env = "MEILI_MAX_UDB_SIZE", default_value = "107374182400")] // 100GB
pub max_udb_size: usize,
#[structopt(long, env = "MEILI_MAX_UDB_SIZE", default_value = "10 GiB")]
pub max_udb_size: Byte,
/// The maximum size, in bytes, of accepted JSON payloads
#[structopt(long, env = "MEILI_HTTP_PAYLOAD_SIZE_LIMIT", default_value = "10485760")] // 10MB
pub http_payload_size_limit: usize,
#[structopt(long, env = "MEILI_HTTP_PAYLOAD_SIZE_LIMIT", default_value = "10 MiB")]
pub http_payload_size_limit: Byte,
/// Read server certificates from CERTFILE.
/// This should contain PEM-format certificates

View File

@ -1,23 +1,20 @@
use std::collections::{BTreeSet, HashSet};
use actix_web::{delete, get, post, put};
use actix_web::{web, HttpResponse};
use indexmap::IndexMap;
use meilisearch_core::{update, MainReader};
use serde_json::Value;
use serde::Deserialize;
use crate::Data;
use crate::error::{Error, ResponseError};
use crate::error::ResponseError;
use crate::helpers::Authentication;
use crate::routes::{IndexParam, IndexUpdateResponse};
use crate::routes::IndexParam;
type Document = IndexMap<String, Value>;
#[derive(Deserialize)]
struct DocumentParam {
index_uid: String,
document_id: String,
_index_uid: String,
_document_id: String,
}
pub fn services(cfg: &mut web::ServiceConfig) {
@ -35,8 +32,8 @@ pub fn services(cfg: &mut web::ServiceConfig) {
wrap = "Authentication::Public"
)]
async fn get_document(
data: web::Data<Data>,
path: web::Path<DocumentParam>,
_data: web::Data<Data>,
_path: web::Path<DocumentParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -46,8 +43,8 @@ async fn get_document(
wrap = "Authentication::Private"
)]
async fn delete_document(
data: web::Data<Data>,
path: web::Path<DocumentParam>,
_data: web::Data<Data>,
_path: web::Path<DocumentParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -55,62 +52,51 @@ async fn delete_document(
#[derive(Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct BrowseQuery {
offset: Option<usize>,
limit: Option<usize>,
attributes_to_retrieve: Option<String>,
}
pub fn get_all_documents_sync(
data: &web::Data<Data>,
reader: &MainReader,
index_uid: &str,
offset: usize,
limit: usize,
attributes_to_retrieve: Option<&String>
) -> Result<Vec<Document>, Error> {
todo!()
_offset: Option<usize>,
_limit: Option<usize>,
_attributes_to_retrieve: Option<String>,
}
#[get("/indexes/{index_uid}/documents", wrap = "Authentication::Public")]
async fn get_all_documents(
data: web::Data<Data>,
path: web::Path<IndexParam>,
params: web::Query<BrowseQuery>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_params: web::Query<BrowseQuery>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
fn find_primary_key(document: &IndexMap<String, Value>) -> Option<String> {
for key in document.keys() {
if key.to_lowercase().contains("id") {
return Some(key.to_string());
}
}
None
}
//fn find_primary_key(document: &IndexMap<String, Value>) -> Option<String> {
//for key in document.keys() {
//if key.to_lowercase().contains("id") {
//return Some(key.to_string());
//}
//}
//None
//}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct UpdateDocumentsQuery {
primary_key: Option<String>,
_primary_key: Option<String>,
}
async fn update_multiple_documents(
data: web::Data<Data>,
path: web::Path<IndexParam>,
params: web::Query<UpdateDocumentsQuery>,
body: web::Json<Vec<Document>>,
is_partial: bool,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_params: web::Query<UpdateDocumentsQuery>,
_body: web::Json<Vec<Document>>,
_is_partial: bool,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[post("/indexes/{index_uid}/documents", wrap = "Authentication::Private")]
async fn add_documents(
data: web::Data<Data>,
path: web::Path<IndexParam>,
params: web::Query<UpdateDocumentsQuery>,
body: web::Json<Vec<Document>>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_params: web::Query<UpdateDocumentsQuery>,
_body: web::Json<Vec<Document>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -130,17 +116,17 @@ async fn update_documents(
wrap = "Authentication::Private"
)]
async fn delete_documents(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Vec<Value>>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_body: web::Json<Vec<Value>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[delete("/indexes/{index_uid}/documents", wrap = "Authentication::Private")]
async fn clear_all_documents(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}

View File

@ -1,14 +1,10 @@
use actix_web::{delete, get, post, put};
use actix_web::{web, HttpResponse};
use chrono::{DateTime, Utc};
use log::error;
use meilisearch_core::{Database, MainReader, UpdateReader};
use meilisearch_core::update::UpdateStatus;
use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize};
use crate::Data;
use crate::error::{Error, ResponseError};
use crate::error::ResponseError;
use crate::helpers::Authentication;
use crate::routes::IndexParam;
@ -22,10 +18,6 @@ pub fn services(cfg: &mut web::ServiceConfig) {
.service(get_all_updates_status);
}
fn generate_uid() -> String {
todo!()
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct IndexResponse {
@ -36,22 +28,15 @@ pub struct IndexResponse {
pub primary_key: Option<String>,
}
pub fn list_indexes_sync(data: &web::Data<Data>, reader: &MainReader) -> Result<Vec<IndexResponse>, ResponseError> {
todo!()
}
#[get("/indexes", wrap = "Authentication::Private")]
async fn list_indexes(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
let reader = data.db.main_read_txn()?;
let indexes = list_indexes_sync(&data, &reader)?;
Ok(HttpResponse::Ok().json(indexes))
async fn list_indexes(_data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[get("/indexes/{index_uid}", wrap = "Authentication::Private")]
async fn get_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -64,20 +49,10 @@ struct IndexCreateRequest {
primary_key: Option<String>,
}
pub fn create_index_sync(
database: &std::sync::Arc<Database>,
uid: String,
name: String,
primary_key: Option<String>,
) -> Result<IndexResponse, Error> {
todo!()
}
#[post("/indexes", wrap = "Authentication::Private")]
async fn create_index(
data: web::Data<Data>,
body: web::Json<IndexCreateRequest>,
_data: web::Data<Data>,
_body: web::Json<IndexCreateRequest>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -101,25 +76,25 @@ struct UpdateIndexResponse {
#[put("/indexes/{index_uid}", wrap = "Authentication::Private")]
async fn update_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<IndexCreateRequest>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_body: web::Json<IndexCreateRequest>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[delete("/indexes/{index_uid}", wrap = "Authentication::Private")]
async fn delete_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[derive(Deserialize)]
struct UpdateParam {
index_uid: String,
update_id: u64,
_index_uid: String,
_update_id: u64,
}
#[get(
@ -127,23 +102,16 @@ struct UpdateParam {
wrap = "Authentication::Private"
)]
async fn get_update_status(
data: web::Data<Data>,
path: web::Path<UpdateParam>,
_data: web::Data<Data>,
_path: web::Path<UpdateParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
pub fn get_all_updates_status_sync(
data: &web::Data<Data>,
reader: &UpdateReader,
index_uid: &str,
) -> Result<Vec<UpdateStatus>, Error> {
todo!()
}
#[get("/indexes/{index_uid}/updates", wrap = "Authentication::Private")]
async fn get_all_updates_status(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}

View File

@ -17,6 +17,6 @@ struct KeysResponse {
}
#[get("/keys", wrap = "Authentication::Admin")]
async fn list(data: web::Data<Data>) -> HttpResponse {
async fn list(_data: web::Data<Data>) -> HttpResponse {
todo!()
}

View File

@ -10,11 +10,11 @@ pub mod setting;
pub mod stats;
pub mod stop_words;
pub mod synonym;
pub mod dump;
//pub mod dump;
#[derive(Deserialize)]
pub struct IndexParam {
index_uid: String,
_index_uid: String,
}
#[derive(Serialize)]

View File

@ -1,19 +1,12 @@
use std::collections::{HashMap, HashSet};
use actix_web::{get, post, web, HttpResponse};
use log::warn;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::error::{Error, FacetCountError, ResponseError};
use crate::helpers::meilisearch::{IndexSearchExt, SearchResult};
use crate::error::ResponseError;
use crate::helpers::Authentication;
use crate::routes::IndexParam;
use crate::Data;
use meilisearch_core::facets::FacetFilter;
use meilisearch_schema::{FieldId, Schema};
pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(search_with_post).service(search_with_url_query);
}
@ -36,9 +29,9 @@ pub struct SearchQuery {
#[get("/indexes/{index_uid}/search", wrap = "Authentication::Public")]
async fn search_with_url_query(
data: web::Data<Data>,
path: web::Path<IndexParam>,
params: web::Query<SearchQuery>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_params: web::Query<SearchQuery>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -46,53 +39,24 @@ async fn search_with_url_query(
#[derive(Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SearchQueryPost {
q: Option<String>,
offset: Option<usize>,
limit: Option<usize>,
attributes_to_retrieve: Option<Vec<String>>,
attributes_to_crop: Option<Vec<String>>,
crop_length: Option<usize>,
attributes_to_highlight: Option<Vec<String>>,
filters: Option<String>,
matches: Option<bool>,
facet_filters: Option<Value>,
facets_distribution: Option<Vec<String>>,
}
impl From<SearchQueryPost> for SearchQuery {
fn from(other: SearchQueryPost) -> SearchQuery {
SearchQuery {
q: other.q,
offset: other.offset,
limit: other.limit,
attributes_to_retrieve: other.attributes_to_retrieve.map(|attrs| attrs.join(",")),
attributes_to_crop: other.attributes_to_crop.map(|attrs| attrs.join(",")),
crop_length: other.crop_length,
attributes_to_highlight: other.attributes_to_highlight.map(|attrs| attrs.join(",")),
filters: other.filters,
matches: other.matches,
facet_filters: other.facet_filters.map(|f| f.to_string()),
facets_distribution: other.facets_distribution.map(|f| format!("{:?}", f)),
}
}
_q: Option<String>,
_offset: Option<usize>,
_limit: Option<usize>,
_attributes_to_retrieve: Option<Vec<String>>,
_attributes_to_crop: Option<Vec<String>>,
_crop_length: Option<usize>,
_attributes_to_highlight: Option<Vec<String>>,
_filters: Option<String>,
_matches: Option<bool>,
_facet_filters: Option<Value>,
_facets_distribution: Option<Vec<String>>,
}
#[post("/indexes/{index_uid}/search", wrap = "Authentication::Public")]
async fn search_with_post(
data: web::Data<Data>,
path: web::Path<IndexParam>,
params: web::Json<SearchQueryPost>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_params: web::Json<SearchQueryPost>,
) -> Result<HttpResponse, ResponseError> {
let query: SearchQuery = params.0.into();
let search_result = query.search(&path.index_uid, data)?;
Ok(HttpResponse::Ok().json(search_result))
}
impl SearchQuery {
fn search(
&self,
index_uid: &str,
data: web::Data<Data>,
) -> Result<SearchResult, ResponseError> {
todo!()
}

View File

@ -1,15 +1,12 @@
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeSet;
use actix_web::{delete, get, post};
use actix_web::{web, HttpResponse};
use meilisearch_core::{MainReader, UpdateWriter};
use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES};
use meilisearch_schema::Schema;
use crate::Data;
use crate::error::{Error, ResponseError};
use crate::error::ResponseError;
use crate::helpers::Authentication;
use crate::routes::{IndexParam, IndexUpdateResponse};
use crate::updates::Settings;
pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(update_all)
@ -32,27 +29,28 @@ pub fn services(cfg: &mut web::ServiceConfig) {
.service(update_attributes_for_faceting);
}
#[post("/indexes/{index_uid}/settings", wrap = "Authentication::Private")]
async fn update_all(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Settings>,
_data: web::Data<Data>,
_path: web::Path<String>,
_body: web::Json<Settings>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[get("/indexes/{index_uid}/settings", wrap = "Authentication::Private")]
async fn get_all(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
#[delete("/indexes/{index_uid}/settings", wrap = "Authentication::Private")]
async fn delete_all(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -62,8 +60,8 @@ async fn delete_all(
wrap = "Authentication::Private"
)]
async fn get_rules(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -73,9 +71,9 @@ async fn get_rules(
wrap = "Authentication::Private"
)]
async fn update_rules(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<Vec<String>>>,
_data: web::Data<Data>,
_path: web::Path<String>,
_body: web::Json<Option<Vec<String>>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -85,8 +83,8 @@ async fn update_rules(
wrap = "Authentication::Private"
)]
async fn delete_rules(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -96,8 +94,8 @@ async fn delete_rules(
wrap = "Authentication::Private"
)]
async fn get_distinct(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -107,9 +105,9 @@ async fn get_distinct(
wrap = "Authentication::Private"
)]
async fn update_distinct(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<String>>,
_data: web::Data<Data>,
_path: web::Path<String>,
_body: web::Json<Option<String>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -119,8 +117,8 @@ async fn update_distinct(
wrap = "Authentication::Private"
)]
async fn delete_distinct(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -130,8 +128,8 @@ async fn delete_distinct(
wrap = "Authentication::Private"
)]
async fn get_searchable(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -141,9 +139,9 @@ async fn get_searchable(
wrap = "Authentication::Private"
)]
async fn update_searchable(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<Vec<String>>>,
_data: web::Data<Data>,
_path: web::Path<String>,
_body: web::Json<Option<Vec<String>>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -153,8 +151,8 @@ async fn update_searchable(
wrap = "Authentication::Private"
)]
async fn delete_searchable(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -164,8 +162,8 @@ async fn delete_searchable(
wrap = "Authentication::Private"
)]
async fn get_displayed(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -175,9 +173,9 @@ async fn get_displayed(
wrap = "Authentication::Private"
)]
async fn update_displayed(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<BTreeSet<String>>>,
_data: web::Data<Data>,
_path: web::Path<String>,
_body: web::Json<Option<BTreeSet<String>>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -187,8 +185,8 @@ async fn update_displayed(
wrap = "Authentication::Private"
)]
async fn delete_displayed(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -198,8 +196,8 @@ async fn delete_displayed(
wrap = "Authentication::Private"
)]
async fn get_attributes_for_faceting(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -209,9 +207,9 @@ async fn get_attributes_for_faceting(
wrap = "Authentication::Private"
)]
async fn update_attributes_for_faceting(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<Option<Vec<String>>>,
_data: web::Data<Data>,
_path: web::Path<String>,
_body: web::Json<Option<Vec<String>>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -221,8 +219,8 @@ async fn update_attributes_for_faceting(
wrap = "Authentication::Private"
)]
async fn delete_attributes_for_faceting(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<String>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}

View File

@ -4,11 +4,9 @@ use actix_web::web;
use actix_web::HttpResponse;
use actix_web::get;
use chrono::{DateTime, Utc};
use log::error;
use serde::Serialize;
use walkdir::WalkDir;
use crate::error::{Error, ResponseError};
use crate::error::ResponseError;
use crate::helpers::Authentication;
use crate::routes::IndexParam;
use crate::Data;
@ -29,8 +27,8 @@ struct IndexStatsResponse {
#[get("/indexes/{index_uid}/stats", wrap = "Authentication::Private")]
async fn index_stats(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -44,7 +42,7 @@ struct StatsResult {
}
#[get("/stats", wrap = "Authentication::Private")]
async fn get_stats(data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
async fn get_stats(_data: web::Data<Data>) -> Result<HttpResponse, ResponseError> {
todo!()
}

View File

@ -1,11 +1,10 @@
use actix_web::{web, HttpResponse};
use actix_web::{delete, get, post};
use meilisearch_core::settings::{SettingsUpdate, UpdateState};
use std::collections::BTreeSet;
use crate::error::{Error, ResponseError};
use crate::error::ResponseError;
use crate::helpers::Authentication;
use crate::routes::{IndexParam, IndexUpdateResponse};
use crate::routes::IndexParam;
use crate::Data;
pub fn services(cfg: &mut web::ServiceConfig) {
@ -17,8 +16,8 @@ pub fn services(cfg: &mut web::ServiceConfig) {
wrap = "Authentication::Private"
)]
async fn get(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -28,9 +27,9 @@ async fn get(
wrap = "Authentication::Private"
)]
async fn update(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<BTreeSet<String>>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_body: web::Json<BTreeSet<String>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -40,8 +39,8 @@ async fn update(
wrap = "Authentication::Private"
)]
async fn delete(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}

View File

@ -2,12 +2,10 @@ use std::collections::BTreeMap;
use actix_web::{web, HttpResponse};
use actix_web::{delete, get, post};
use indexmap::IndexMap;
use meilisearch_core::settings::{SettingsUpdate, UpdateState};
use crate::error::{Error, ResponseError};
use crate::error::ResponseError;
use crate::helpers::Authentication;
use crate::routes::{IndexParam, IndexUpdateResponse};
use crate::routes::IndexParam;
use crate::Data;
pub fn services(cfg: &mut web::ServiceConfig) {
@ -19,8 +17,8 @@ pub fn services(cfg: &mut web::ServiceConfig) {
wrap = "Authentication::Private"
)]
async fn get(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -30,9 +28,9 @@ async fn get(
wrap = "Authentication::Private"
)]
async fn update(
data: web::Data<Data>,
path: web::Path<IndexParam>,
body: web::Json<BTreeMap<String, Vec<String>>>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
_body: web::Json<BTreeMap<String, Vec<String>>>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}
@ -42,8 +40,8 @@ async fn update(
wrap = "Authentication::Private"
)]
async fn delete(
data: web::Data<Data>,
path: web::Path<IndexParam>,
_data: web::Data<Data>,
_path: web::Path<IndexParam>,
) -> Result<HttpResponse, ResponseError> {
todo!()
}

17
src/updates/mod.rs Normal file
View File

@ -0,0 +1,17 @@
mod settings;
pub use settings::{Settings, Facets};
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
enum UpdateMeta {
DocumentsAddition { method: String, format: String },
ClearDocuments,
Settings(Settings),
Facets(Facets),
}
#[derive(Clone, Debug)]
pub struct UpdateQueue;

51
src/updates/settings.rs Normal file
View File

@ -0,0 +1,51 @@
use std::num::NonZeroUsize;
use std::collections::HashMap;
use serde::{Serialize, Deserialize, de::Deserializer};
// Any value that is present is considered Some value, including null.
fn deserialize_some<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where T: Deserialize<'de>,
D: Deserializer<'de>
{
Deserialize::deserialize(deserializer).map(Some)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct Settings {
#[serde(
default,
deserialize_with = "deserialize_some",
skip_serializing_if = "Option::is_none",
)]
displayed_attributes: Option<Option<Vec<String>>>,
#[serde(
default,
deserialize_with = "deserialize_some",
skip_serializing_if = "Option::is_none",
)]
searchable_attributes: Option<Option<Vec<String>>>,
#[serde(default)]
faceted_attributes: Option<HashMap<String, String>>,
#[serde(
default,
deserialize_with = "deserialize_some",
skip_serializing_if = "Option::is_none",
)]
criteria: Option<Option<Vec<String>>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct Facets {
level_group_size: Option<NonZeroUsize>,
min_level_size: Option<NonZeroUsize>,
}