mirror of
https://github.com/meilisearch/meilisearch.git
synced 2024-11-26 20:15:07 +08:00
Merge pull request #311 from meilisearch/add-index-name-and-id
Add index name and change some routes request body & response
This commit is contained in:
commit
4abea919b2
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -839,7 +839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilidb-core"
|
name = "meilidb-core"
|
||||||
version = "0.7.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arc-swap 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -856,9 +856,9 @@ dependencies = [
|
|||||||
"indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"levenshtein_automata 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"levenshtein_automata 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"meilidb-schema 0.6.0",
|
"meilidb-schema 0.8.0",
|
||||||
"meilidb-tokenizer 0.6.1",
|
"meilidb-tokenizer 0.8.0",
|
||||||
"meilidb-types 0.1.0",
|
"meilidb-types 0.8.0",
|
||||||
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustyline 5.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustyline 5.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -876,7 +876,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilidb-http"
|
name = "meilidb-http"
|
||||||
version = "0.3.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-compression 0.1.0-alpha.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"async-compression 0.1.0-alpha.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -889,8 +889,8 @@ dependencies = [
|
|||||||
"jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"main_error 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"main_error 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"meilidb-core 0.7.0",
|
"meilidb-core 0.8.0",
|
||||||
"meilidb-schema 0.6.0",
|
"meilidb-schema 0.8.0",
|
||||||
"pretty-bytes 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pretty-bytes 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -908,7 +908,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilidb-schema"
|
name = "meilidb-schema"
|
||||||
version = "0.6.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -919,7 +919,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilidb-tokenizer"
|
name = "meilidb-tokenizer"
|
||||||
version = "0.6.1"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deunicode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"deunicode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slice-group-by 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slice-group-by 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -927,7 +927,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilidb-types"
|
name = "meilidb-types"
|
||||||
version = "0.1.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"zerocopy 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zerocopy 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -22,7 +22,7 @@ struct IndexCommand {
|
|||||||
database_path: PathBuf,
|
database_path: PathBuf,
|
||||||
|
|
||||||
#[structopt(long, default_value = "default")]
|
#[structopt(long, default_value = "default")]
|
||||||
index_name: String,
|
index_uid: String,
|
||||||
|
|
||||||
/// The csv file to index.
|
/// The csv file to index.
|
||||||
#[structopt(parse(from_os_str))]
|
#[structopt(parse(from_os_str))]
|
||||||
@ -46,7 +46,7 @@ struct SearchCommand {
|
|||||||
database_path: PathBuf,
|
database_path: PathBuf,
|
||||||
|
|
||||||
#[structopt(long, default_value = "default")]
|
#[structopt(long, default_value = "default")]
|
||||||
index_name: String,
|
index_uid: String,
|
||||||
|
|
||||||
/// Timeout after which the search will return results.
|
/// Timeout after which the search will return results.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
@ -76,7 +76,7 @@ struct ShowUpdatesCommand {
|
|||||||
database_path: PathBuf,
|
database_path: PathBuf,
|
||||||
|
|
||||||
#[structopt(long, default_value = "default")]
|
#[structopt(long, default_value = "default")]
|
||||||
index_name: String,
|
index_uid: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
@ -106,9 +106,9 @@ fn index_command(command: IndexCommand, database: Database) -> Result<(), Box<dy
|
|||||||
let (sender, receiver) = mpsc::sync_channel(100);
|
let (sender, receiver) = mpsc::sync_channel(100);
|
||||||
let update_fn =
|
let update_fn =
|
||||||
move |_name: &str, update: ProcessedUpdateResult| sender.send(update.update_id).unwrap();
|
move |_name: &str, update: ProcessedUpdateResult| sender.send(update.update_id).unwrap();
|
||||||
let index = match database.open_index(&command.index_name) {
|
let index = match database.open_index(&command.index_uid) {
|
||||||
Some(index) => index,
|
Some(index) => index,
|
||||||
None => database.create_index(&command.index_name).unwrap(),
|
None => database.create_index(&command.index_uid).unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
database.set_update_callback(Box::new(update_fn));
|
database.set_update_callback(Box::new(update_fn));
|
||||||
@ -318,7 +318,7 @@ fn crop_text(
|
|||||||
fn search_command(command: SearchCommand, database: Database) -> Result<(), Box<dyn Error>> {
|
fn search_command(command: SearchCommand, database: Database) -> Result<(), Box<dyn Error>> {
|
||||||
let env = &database.env;
|
let env = &database.env;
|
||||||
let index = database
|
let index = database
|
||||||
.open_index(&command.index_name)
|
.open_index(&command.index_uid)
|
||||||
.expect("Could not find index");
|
.expect("Could not find index");
|
||||||
|
|
||||||
let reader = env.read_txn().unwrap();
|
let reader = env.read_txn().unwrap();
|
||||||
@ -446,7 +446,7 @@ fn show_updates_command(
|
|||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let env = &database.env;
|
let env = &database.env;
|
||||||
let index = database
|
let index = database
|
||||||
.open_index(&command.index_name)
|
.open_index(&command.index_uid)
|
||||||
.expect("Could not find index");
|
.expect("Could not find index");
|
||||||
|
|
||||||
let reader = env.read_txn().unwrap();
|
let reader = env.read_txn().unwrap();
|
||||||
|
@ -45,7 +45,7 @@ pub type UpdateEventsEmitter = Sender<UpdateEvent>;
|
|||||||
fn update_awaiter(
|
fn update_awaiter(
|
||||||
receiver: UpdateEvents,
|
receiver: UpdateEvents,
|
||||||
env: heed::Env,
|
env: heed::Env,
|
||||||
index_name: &str,
|
index_uid: &str,
|
||||||
update_fn: Arc<ArcSwapFn>,
|
update_fn: Arc<ArcSwapFn>,
|
||||||
index: Index,
|
index: Index,
|
||||||
) {
|
) {
|
||||||
@ -91,7 +91,7 @@ fn update_awaiter(
|
|||||||
|
|
||||||
// call the user callback when the update and the result are written consistently
|
// call the user callback when the update and the result are written consistently
|
||||||
if let Some(ref callback) = *update_fn.load() {
|
if let Some(ref callback) = *update_fn.load() {
|
||||||
(callback)(index_name, status);
|
(callback)(index_uid, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,22 +116,22 @@ impl Database {
|
|||||||
let mut must_open = Vec::new();
|
let mut must_open = Vec::new();
|
||||||
let reader = env.read_txn()?;
|
let reader = env.read_txn()?;
|
||||||
for result in indexes_store.iter(&reader)? {
|
for result in indexes_store.iter(&reader)? {
|
||||||
let (index_name, _) = result?;
|
let (index_uid, _) = result?;
|
||||||
must_open.push(index_name.to_owned());
|
must_open.push(index_uid.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.abort();
|
reader.abort();
|
||||||
|
|
||||||
// open the previously aggregated indexes
|
// open the previously aggregated indexes
|
||||||
let mut indexes = HashMap::new();
|
let mut indexes = HashMap::new();
|
||||||
for index_name in must_open {
|
for index_uid in must_open {
|
||||||
let (sender, receiver) = crossbeam_channel::bounded(100);
|
let (sender, receiver) = crossbeam_channel::bounded(100);
|
||||||
let index = match store::open(&env, &index_name, sender.clone())? {
|
let index = match store::open(&env, &index_uid, sender.clone())? {
|
||||||
Some(index) => index,
|
Some(index) => index,
|
||||||
None => {
|
None => {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"the index {} doesn't exist or has not all the databases",
|
"the index {} doesn't exist or has not all the databases",
|
||||||
index_name
|
index_uid
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ impl Database {
|
|||||||
|
|
||||||
let env_clone = env.clone();
|
let env_clone = env.clone();
|
||||||
let index_clone = index.clone();
|
let index_clone = index.clone();
|
||||||
let name_clone = index_name.clone();
|
let name_clone = index_uid.clone();
|
||||||
let update_fn_clone = update_fn.clone();
|
let update_fn_clone = update_fn.clone();
|
||||||
|
|
||||||
let handle = thread::spawn(move || {
|
let handle = thread::spawn(move || {
|
||||||
@ -156,7 +156,7 @@ impl Database {
|
|||||||
// possible pre-boot updates are consumed
|
// possible pre-boot updates are consumed
|
||||||
sender.send(UpdateEvent::NewUpdate).unwrap();
|
sender.send(UpdateEvent::NewUpdate).unwrap();
|
||||||
|
|
||||||
let result = indexes.insert(index_name, (index, handle));
|
let result = indexes.insert(index_uid, (index, handle));
|
||||||
assert!(
|
assert!(
|
||||||
result.is_none(),
|
result.is_none(),
|
||||||
"The index should not have been already open"
|
"The index should not have been already open"
|
||||||
@ -251,9 +251,9 @@ impl Database {
|
|||||||
self.env.copy_to_path(path, CompactionOption::Enabled)
|
self.env.copy_to_path(path, CompactionOption::Enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indexes_names(&self) -> MResult<Vec<String>> {
|
pub fn indexes_uids(&self) -> Vec<String> {
|
||||||
let indexes = self.indexes.read().unwrap();
|
let indexes = self.indexes.read().unwrap();
|
||||||
Ok(indexes.keys().cloned().collect())
|
indexes.keys().cloned().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn common_store(&self) -> heed::PolyDatabase {
|
pub fn common_store(&self) -> heed::PolyDatabase {
|
||||||
|
@ -1,17 +1,27 @@
|
|||||||
use crate::RankedMap;
|
use crate::RankedMap;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
use heed::types::{ByteSlice, OwnedType, SerdeBincode, Str};
|
use heed::types::{ByteSlice, OwnedType, SerdeBincode, Str};
|
||||||
use heed::Result as ZResult;
|
use heed::Result as ZResult;
|
||||||
use meilidb_schema::Schema;
|
use meilidb_schema::Schema;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
const CREATED_AT_KEY: &str = "created-at";
|
||||||
const CUSTOMS_KEY: &str = "customs-key";
|
const CUSTOMS_KEY: &str = "customs-key";
|
||||||
|
const FIELDS_FREQUENCY_KEY: &str = "fields-frequency";
|
||||||
|
const NAME_KEY: &str = "name";
|
||||||
const NUMBER_OF_DOCUMENTS_KEY: &str = "number-of-documents";
|
const NUMBER_OF_DOCUMENTS_KEY: &str = "number-of-documents";
|
||||||
const RANKED_MAP_KEY: &str = "ranked-map";
|
const RANKED_MAP_KEY: &str = "ranked-map";
|
||||||
const SCHEMA_KEY: &str = "schema";
|
const SCHEMA_KEY: &str = "schema";
|
||||||
const SYNONYMS_KEY: &str = "synonyms";
|
|
||||||
const STOP_WORDS_KEY: &str = "stop-words";
|
const STOP_WORDS_KEY: &str = "stop-words";
|
||||||
|
const SYNONYMS_KEY: &str = "synonyms";
|
||||||
|
const UPDATED_AT_KEY: &str = "updated-at";
|
||||||
const WORDS_KEY: &str = "words";
|
const WORDS_KEY: &str = "words";
|
||||||
|
|
||||||
|
pub type FreqsMap = HashMap<String, usize>;
|
||||||
|
type SerdeFreqsMap = SerdeBincode<FreqsMap>;
|
||||||
|
type SerdeDatetime = SerdeBincode<DateTime<Utc>>;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Main {
|
pub struct Main {
|
||||||
pub(crate) main: heed::PolyDatabase,
|
pub(crate) main: heed::PolyDatabase,
|
||||||
@ -22,6 +32,35 @@ impl Main {
|
|||||||
self.main.clear(writer)
|
self.main.clear(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn put_name(self, writer: &mut heed::RwTxn, name: &str) -> ZResult<()> {
|
||||||
|
self.main.put::<Str, Str>(writer, NAME_KEY, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(self, reader: &heed::RoTxn) -> ZResult<Option<String>> {
|
||||||
|
Ok(self
|
||||||
|
.main
|
||||||
|
.get::<Str, Str>(reader, NAME_KEY)?
|
||||||
|
.map(|name| name.to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_created_at(self, writer: &mut heed::RwTxn) -> ZResult<()> {
|
||||||
|
self.main
|
||||||
|
.put::<Str, SerdeDatetime>(writer, CREATED_AT_KEY, &Utc::now())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn created_at(self, reader: &heed::RoTxn) -> ZResult<Option<DateTime<Utc>>> {
|
||||||
|
self.main.get::<Str, SerdeDatetime>(reader, CREATED_AT_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_updated_at(self, writer: &mut heed::RwTxn) -> ZResult<()> {
|
||||||
|
self.main
|
||||||
|
.put::<Str, SerdeDatetime>(writer, UPDATED_AT_KEY, &Utc::now())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updated_at(self, reader: &heed::RoTxn) -> ZResult<Option<DateTime<Utc>>> {
|
||||||
|
self.main.get::<Str, SerdeDatetime>(reader, UPDATED_AT_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn put_words_fst(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> ZResult<()> {
|
pub fn put_words_fst(self, writer: &mut heed::RwTxn, fst: &fst::Set) -> ZResult<()> {
|
||||||
let bytes = fst.as_fst().as_bytes();
|
let bytes = fst.as_fst().as_bytes();
|
||||||
self.main.put::<Str, ByteSlice>(writer, WORDS_KEY, bytes)
|
self.main.put::<Str, ByteSlice>(writer, WORDS_KEY, bytes)
|
||||||
@ -114,6 +153,25 @@ impl Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn put_fields_frequency(
|
||||||
|
self,
|
||||||
|
writer: &mut heed::RwTxn,
|
||||||
|
fields_frequency: &FreqsMap,
|
||||||
|
) -> ZResult<()> {
|
||||||
|
self.main
|
||||||
|
.put::<Str, SerdeFreqsMap>(writer, FIELDS_FREQUENCY_KEY, fields_frequency)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fields_frequency(&self, reader: &heed::RoTxn) -> ZResult<Option<FreqsMap>> {
|
||||||
|
match self
|
||||||
|
.main
|
||||||
|
.get::<Str, SerdeFreqsMap>(reader, FIELDS_FREQUENCY_KEY)?
|
||||||
|
{
|
||||||
|
Some(freqs) => Ok(Some(freqs)),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn put_customs(self, writer: &mut heed::RwTxn, customs: &[u8]) -> ZResult<()> {
|
pub fn put_customs(self, writer: &mut heed::RwTxn, customs: &[u8]) -> ZResult<()> {
|
||||||
self.main
|
self.main
|
||||||
.put::<Str, ByteSlice>(writer, CUSTOMS_KEY, customs)
|
.put::<Str, ByteSlice>(writer, CUSTOMS_KEY, customs)
|
||||||
|
@ -4,15 +4,15 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use heed::types::{SerdeBincode, Str};
|
use heed::types::{SerdeBincode, Str};
|
||||||
use log::*;
|
use log::error;
|
||||||
use meilidb_core::{Database, MResult};
|
use meilidb_core::{Database, Error as MError, MResult};
|
||||||
use sysinfo::Pid;
|
use sysinfo::Pid;
|
||||||
|
|
||||||
use crate::option::Opt;
|
use crate::option::Opt;
|
||||||
use crate::routes::index::index_update_callback;
|
use crate::routes::index::index_update_callback;
|
||||||
|
|
||||||
pub type FreqsMap = HashMap<String, usize>;
|
const LAST_UPDATE_KEY: &str = "last-update";
|
||||||
type SerdeFreqsMap = SerdeBincode<FreqsMap>;
|
|
||||||
type SerdeDatetime = SerdeBincode<DateTime<Utc>>;
|
type SerdeDatetime = SerdeBincode<DateTime<Utc>>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -44,51 +44,29 @@ impl DataInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_update(
|
pub fn last_update(&self, reader: &heed::RoTxn) -> MResult<Option<DateTime<Utc>>> {
|
||||||
&self,
|
|
||||||
reader: &heed::RoTxn,
|
|
||||||
index_name: &str,
|
|
||||||
) -> MResult<Option<DateTime<Utc>>> {
|
|
||||||
let key = format!("last-update-{}", index_name);
|
|
||||||
match self
|
match self
|
||||||
.db
|
.db
|
||||||
.common_store()
|
.common_store()
|
||||||
.get::<Str, SerdeDatetime>(&reader, &key)?
|
.get::<Str, SerdeDatetime>(reader, LAST_UPDATE_KEY)?
|
||||||
{
|
{
|
||||||
Some(datetime) => Ok(Some(datetime)),
|
Some(datetime) => Ok(Some(datetime)),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_last_update(&self, writer: &mut heed::RwTxn, index_name: &str) -> MResult<()> {
|
pub fn set_last_update(&self, writer: &mut heed::RwTxn) -> MResult<()> {
|
||||||
let key = format!("last-update-{}", index_name);
|
|
||||||
self.db
|
self.db
|
||||||
.common_store()
|
.common_store()
|
||||||
.put::<Str, SerdeDatetime>(writer, &key, &Utc::now())
|
.put::<Str, SerdeDatetime>(writer, LAST_UPDATE_KEY, &Utc::now())
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fields_frequency(
|
pub fn compute_stats(&self, writer: &mut heed::RwTxn, index_uid: &str) -> MResult<()> {
|
||||||
&self,
|
let index = match self.db.open_index(&index_uid) {
|
||||||
reader: &heed::RoTxn,
|
|
||||||
index_name: &str,
|
|
||||||
) -> MResult<Option<FreqsMap>> {
|
|
||||||
let key = format!("fields-frequency-{}", index_name);
|
|
||||||
match self
|
|
||||||
.db
|
|
||||||
.common_store()
|
|
||||||
.get::<Str, SerdeFreqsMap>(&reader, &key)?
|
|
||||||
{
|
|
||||||
Some(freqs) => Ok(Some(freqs)),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compute_stats(&self, writer: &mut heed::RwTxn, index_name: &str) -> MResult<()> {
|
|
||||||
let index = match self.db.open_index(&index_name) {
|
|
||||||
Some(index) => index,
|
Some(index) => index,
|
||||||
None => {
|
None => {
|
||||||
error!("Impossible to retrieve index {}", index_name);
|
error!("Impossible to retrieve index {}", index_uid);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -115,12 +93,10 @@ impl DataInner {
|
|||||||
.map(|(a, c)| (schema.attribute_name(a).to_owned(), c))
|
.map(|(a, c)| (schema.attribute_name(a).to_owned(), c))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let key = format!("fields-frequency-{}", index_name);
|
index
|
||||||
self.db
|
.main
|
||||||
.common_store()
|
.put_fields_frequency(writer, &frequency)
|
||||||
.put::<Str, SerdeFreqsMap>(writer, &key, &frequency)?;
|
.map_err(MError::Zlmdb)
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,8 +120,8 @@ impl Data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let callback_context = data.clone();
|
let callback_context = data.clone();
|
||||||
db.set_update_callback(Box::new(move |index_name, status| {
|
db.set_update_callback(Box::new(move |index_uid, status| {
|
||||||
index_update_callback(&index_name, &callback_context, status);
|
index_update_callback(&index_uid, &callback_context, status);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
data
|
data
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::routes::setting::{RankingOrdering, SettingBody};
|
use crate::routes::setting::{RankingOrdering, SettingBody};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use log::*;
|
use log::error;
|
||||||
use meilidb_core::criterion::*;
|
use meilidb_core::criterion::*;
|
||||||
use meilidb_core::Highlight;
|
use meilidb_core::Highlight;
|
||||||
use meilidb_core::{Index, RankedMap};
|
use meilidb_core::{Index, RankedMap};
|
||||||
|
@ -38,9 +38,9 @@ impl ContextExt for Context<Data> {
|
|||||||
.common_store()
|
.common_store()
|
||||||
.get::<Str, SerdeBincode<Token>>(&reader, &token_key)
|
.get::<Str, SerdeBincode<Token>>(&reader, &token_key)
|
||||||
.map_err(ResponseError::internal)?
|
.map_err(ResponseError::internal)?
|
||||||
.ok_or(ResponseError::not_found(format!(
|
.ok_or(ResponseError::invalid_token(format!(
|
||||||
"token key: {}",
|
"Api key does not exist: {}",
|
||||||
token_key
|
user_api_key
|
||||||
)))?;
|
)))?;
|
||||||
|
|
||||||
if token_config.revoked {
|
if token_config.revoked {
|
||||||
@ -93,12 +93,12 @@ impl ContextExt for Context<Data> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn index(&self) -> Result<Index, ResponseError> {
|
fn index(&self) -> Result<Index, ResponseError> {
|
||||||
let index_name = self.url_param("index")?;
|
let index_uid = self.url_param("index")?;
|
||||||
let index = self
|
let index = self
|
||||||
.state()
|
.state()
|
||||||
.db
|
.db
|
||||||
.open_index(&index_name)
|
.open_index(&index_uid)
|
||||||
.ok_or(ResponseError::index_not_found(index_name))?;
|
.ok_or(ResponseError::index_not_found(index_uid))?;
|
||||||
Ok(index)
|
Ok(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ struct BrowseQuery {
|
|||||||
attributes_to_retrieve: Option<String>,
|
attributes_to_retrieve: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn browse_documents(ctx: Context<Data>) -> SResult<Response> {
|
pub async fn get_all_documents(ctx: Context<Data>) -> SResult<Response> {
|
||||||
ctx.is_allowed(DocumentsRead)?;
|
ctx.is_allowed(DocumentsRead)?;
|
||||||
|
|
||||||
let index = ctx.index()?;
|
let index = ctx.index()?;
|
||||||
@ -114,15 +114,7 @@ pub async fn browse_documents(ctx: Context<Data>) -> SResult<Response> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if response_body.is_empty() {
|
Ok(tide::response::json(response_body))
|
||||||
Ok(tide::response::json(response_body)
|
|
||||||
.with_status(StatusCode::NO_CONTENT)
|
|
||||||
.into_response())
|
|
||||||
} else {
|
|
||||||
Ok(tide::response::json(response_body)
|
|
||||||
.with_status(StatusCode::OK)
|
|
||||||
.into_response())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infered_schema(document: &IndexMap<String, Value>) -> Option<meilidb_schema::Schema> {
|
fn infered_schema(document: &IndexMap<String, Value>) -> Option<meilidb_schema::Schema> {
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
|
use log::error;
|
||||||
use meilidb_core::ProcessedUpdateResult;
|
use meilidb_core::ProcessedUpdateResult;
|
||||||
use meilidb_schema::Schema;
|
use meilidb_schema::{Schema, SchemaBuilder};
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use tide::querystring::ContextExt as QSContextExt;
|
||||||
use tide::response::IntoResponse;
|
use tide::response::IntoResponse;
|
||||||
use tide::{Context, Response};
|
use tide::{Context, Response};
|
||||||
|
|
||||||
@ -12,17 +17,74 @@ use crate::models::token::ACL::*;
|
|||||||
use crate::routes::document::IndexUpdateResponse;
|
use crate::routes::document::IndexUpdateResponse;
|
||||||
use crate::Data;
|
use crate::Data;
|
||||||
|
|
||||||
pub async fn list_indexes(ctx: Context<Data>) -> SResult<Response> {
|
fn generate_uid() -> String {
|
||||||
ctx.is_allowed(IndexesRead)?;
|
let mut rng = rand::thread_rng();
|
||||||
let list = ctx
|
let sample = b"abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
.state()
|
sample
|
||||||
.db
|
.choose_multiple(&mut rng, 8)
|
||||||
.indexes_names()
|
.map(|c| *c as char)
|
||||||
.map_err(ResponseError::internal)?;
|
.collect()
|
||||||
Ok(tide::response::json(list))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_index_schema(ctx: Context<Data>) -> SResult<Response> {
|
pub async fn list_indexes(ctx: Context<Data>) -> SResult<Response> {
|
||||||
|
ctx.is_allowed(IndexesRead)?;
|
||||||
|
|
||||||
|
let indexes_uids = ctx.state().db.indexes_uids();
|
||||||
|
|
||||||
|
let env = &ctx.state().db.env;
|
||||||
|
let reader = env.read_txn().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
let mut response_body = Vec::new();
|
||||||
|
|
||||||
|
for index_uid in indexes_uids {
|
||||||
|
let index = ctx.state().db.open_index(&index_uid);
|
||||||
|
|
||||||
|
match index {
|
||||||
|
Some(index) => {
|
||||||
|
let name = index
|
||||||
|
.main
|
||||||
|
.name(&reader)
|
||||||
|
.map_err(ResponseError::internal)?
|
||||||
|
.ok_or(ResponseError::internal("'name' not found"))?;
|
||||||
|
let created_at = index
|
||||||
|
.main
|
||||||
|
.created_at(&reader)
|
||||||
|
.map_err(ResponseError::internal)?
|
||||||
|
.ok_or(ResponseError::internal("'created_at' date not found"))?;
|
||||||
|
let updated_at = index
|
||||||
|
.main
|
||||||
|
.updated_at(&reader)
|
||||||
|
.map_err(ResponseError::internal)?
|
||||||
|
.ok_or(ResponseError::internal("'updated_at' date not found"))?;
|
||||||
|
|
||||||
|
let index_reponse = IndexResponse {
|
||||||
|
name,
|
||||||
|
uid: index_uid,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
};
|
||||||
|
response_body.push(index_reponse);
|
||||||
|
}
|
||||||
|
None => error!(
|
||||||
|
"Index {} is referenced in the indexes list but cannot be found",
|
||||||
|
index_uid
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tide::response::json(response_body))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct IndexResponse {
|
||||||
|
name: String,
|
||||||
|
uid: String,
|
||||||
|
created_at: DateTime<Utc>,
|
||||||
|
updated_at: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_index(ctx: Context<Data>) -> SResult<Response> {
|
||||||
ctx.is_allowed(IndexesRead)?;
|
ctx.is_allowed(IndexesRead)?;
|
||||||
|
|
||||||
let index = ctx.index()?;
|
let index = ctx.index()?;
|
||||||
@ -30,41 +92,65 @@ pub async fn get_index_schema(ctx: Context<Data>) -> SResult<Response> {
|
|||||||
let env = &ctx.state().db.env;
|
let env = &ctx.state().db.env;
|
||||||
let reader = env.read_txn().map_err(ResponseError::internal)?;
|
let reader = env.read_txn().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
let schema = index
|
let uid = ctx.url_param("index")?;
|
||||||
|
let name = index
|
||||||
.main
|
.main
|
||||||
.schema(&reader)
|
.name(&reader)
|
||||||
.map_err(ResponseError::create_index)?;
|
.map_err(ResponseError::internal)?
|
||||||
|
.ok_or(ResponseError::internal("'name' not found"))?;
|
||||||
|
let created_at = index
|
||||||
|
.main
|
||||||
|
.created_at(&reader)
|
||||||
|
.map_err(ResponseError::internal)?
|
||||||
|
.ok_or(ResponseError::internal("'created_at' date not found"))?;
|
||||||
|
let updated_at = index
|
||||||
|
.main
|
||||||
|
.updated_at(&reader)
|
||||||
|
.map_err(ResponseError::internal)?
|
||||||
|
.ok_or(ResponseError::internal("'updated_at' date not found"))?;
|
||||||
|
|
||||||
match schema {
|
let response_body = IndexResponse {
|
||||||
Some(schema) => {
|
name,
|
||||||
let schema = SchemaBody::from(schema);
|
uid,
|
||||||
Ok(tide::response::json(schema))
|
created_at,
|
||||||
}
|
updated_at,
|
||||||
None => Ok(
|
};
|
||||||
tide::response::json(json!({ "message": "missing index schema" }))
|
|
||||||
.with_status(StatusCode::NOT_FOUND)
|
Ok(tide::response::json(response_body))
|
||||||
.into_response(),
|
}
|
||||||
),
|
|
||||||
}
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
|
struct IndexCreateRequest {
|
||||||
|
name: String,
|
||||||
|
schema: Option<SchemaBody>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct IndexCreateResponse {
|
||||||
|
name: String,
|
||||||
|
uid: String,
|
||||||
|
schema: Option<SchemaBody>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
update_id: Option<u64>,
|
||||||
|
created_at: DateTime<Utc>,
|
||||||
|
updated_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_index(mut ctx: Context<Data>) -> SResult<Response> {
|
pub async fn create_index(mut ctx: Context<Data>) -> SResult<Response> {
|
||||||
ctx.is_allowed(IndexesWrite)?;
|
ctx.is_allowed(IndexesWrite)?;
|
||||||
|
|
||||||
let index_name = ctx.url_param("index")?;
|
let body = ctx
|
||||||
|
.body_json::<IndexCreateRequest>()
|
||||||
|
.await
|
||||||
|
.map_err(ResponseError::bad_request)?;
|
||||||
|
|
||||||
let body = ctx.body_bytes().await.map_err(ResponseError::bad_request)?;
|
let generated_uid = generate_uid();
|
||||||
let schema: Option<Schema> = if body.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
serde_json::from_slice::<SchemaBody>(&body)
|
|
||||||
.map_err(ResponseError::bad_request)
|
|
||||||
.map(|s| Some(s.into()))?
|
|
||||||
};
|
|
||||||
|
|
||||||
let db = &ctx.state().db;
|
let db = &ctx.state().db;
|
||||||
|
|
||||||
let created_index = match db.create_index(&index_name) {
|
let created_index = match db.create_index(&generated_uid) {
|
||||||
Ok(index) => index,
|
Ok(index) => index,
|
||||||
Err(e) => return Err(ResponseError::create_index(e)),
|
Err(e) => return Err(ResponseError::create_index(e)),
|
||||||
};
|
};
|
||||||
@ -72,44 +158,172 @@ pub async fn create_index(mut ctx: Context<Data>) -> SResult<Response> {
|
|||||||
let env = &db.env;
|
let env = &db.env;
|
||||||
let mut writer = env.write_txn().map_err(ResponseError::internal)?;
|
let mut writer = env.write_txn().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
created_index
|
||||||
|
.main
|
||||||
|
.put_name(&mut writer, &body.name)
|
||||||
|
.map_err(ResponseError::internal)?;
|
||||||
|
created_index
|
||||||
|
.main
|
||||||
|
.put_created_at(&mut writer)
|
||||||
|
.map_err(ResponseError::internal)?;
|
||||||
|
created_index
|
||||||
|
.main
|
||||||
|
.put_updated_at(&mut writer)
|
||||||
|
.map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
let schema: Option<Schema> = body.schema.clone().map(Into::into);
|
||||||
|
let mut response_update_id = None;
|
||||||
|
if let Some(schema) = schema {
|
||||||
|
let update_id = created_index
|
||||||
|
.schema_update(&mut writer, schema)
|
||||||
|
.map_err(ResponseError::internal)?;
|
||||||
|
response_update_id = Some(update_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.commit().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
let response_body = IndexCreateResponse {
|
||||||
|
name: body.name,
|
||||||
|
uid: generated_uid,
|
||||||
|
schema: body.schema,
|
||||||
|
update_id: response_update_id,
|
||||||
|
created_at: Utc::now(),
|
||||||
|
updated_at: Utc::now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(tide::response::json(response_body)
|
||||||
|
.with_status(StatusCode::CREATED)
|
||||||
|
.into_response())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
|
struct UpdateIndexRequest {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct UpdateIndexResponse {
|
||||||
|
name: String,
|
||||||
|
uid: String,
|
||||||
|
created_at: DateTime<Utc>,
|
||||||
|
updated_at: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_index(mut ctx: Context<Data>) -> SResult<Response> {
|
||||||
|
ctx.is_allowed(IndexesWrite)?;
|
||||||
|
|
||||||
|
let body = ctx
|
||||||
|
.body_json::<UpdateIndexRequest>()
|
||||||
|
.await
|
||||||
|
.map_err(ResponseError::bad_request)?;
|
||||||
|
|
||||||
|
let index_uid = ctx.url_param("index")?;
|
||||||
|
let index = ctx.index()?;
|
||||||
|
|
||||||
|
let db = &ctx.state().db;
|
||||||
|
|
||||||
|
let env = &db.env;
|
||||||
|
let mut writer = env.write_txn().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
index
|
||||||
|
.main
|
||||||
|
.put_name(&mut writer, &body.name)
|
||||||
|
.map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
index
|
||||||
|
.main
|
||||||
|
.put_updated_at(&mut writer)
|
||||||
|
.map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
writer.commit().map_err(ResponseError::internal)?;
|
||||||
|
let reader = env.read_txn().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
let created_at = index
|
||||||
|
.main
|
||||||
|
.created_at(&reader)
|
||||||
|
.map_err(ResponseError::internal)?
|
||||||
|
.ok_or(ResponseError::internal("'created_at' date not found"))?;
|
||||||
|
let updated_at = index
|
||||||
|
.main
|
||||||
|
.updated_at(&reader)
|
||||||
|
.map_err(ResponseError::internal)?
|
||||||
|
.ok_or(ResponseError::internal("'updated_at' date not found"))?;
|
||||||
|
|
||||||
|
let response_body = UpdateIndexResponse {
|
||||||
|
name: body.name,
|
||||||
|
uid: index_uid,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(tide::response::json(response_body)
|
||||||
|
.with_status(StatusCode::ACCEPTED)
|
||||||
|
.into_response())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||||
|
struct SchemaParams {
|
||||||
|
raw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_index_schema(ctx: Context<Data>) -> SResult<Response> {
|
||||||
|
ctx.is_allowed(IndexesRead)?;
|
||||||
|
|
||||||
|
let index = ctx.index()?;
|
||||||
|
|
||||||
|
// Tide doesn't support "no query param"
|
||||||
|
let params: SchemaParams = ctx.url_query().unwrap_or_default();
|
||||||
|
|
||||||
|
let env = &ctx.state().db.env;
|
||||||
|
let reader = env.read_txn().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
|
let schema = index
|
||||||
|
.main
|
||||||
|
.schema(&reader)
|
||||||
|
.map_err(ResponseError::open_index)?;
|
||||||
|
|
||||||
match schema {
|
match schema {
|
||||||
Some(schema) => {
|
Some(schema) => {
|
||||||
let update_id = created_index
|
if params.raw {
|
||||||
.schema_update(&mut writer, schema.clone())
|
Ok(tide::response::json(schema))
|
||||||
.map_err(ResponseError::internal)?;
|
} else {
|
||||||
|
Ok(tide::response::json(SchemaBody::from(schema)))
|
||||||
writer.commit().map_err(ResponseError::internal)?;
|
}
|
||||||
|
|
||||||
let response_body = IndexUpdateResponse { update_id };
|
|
||||||
Ok(tide::response::json(response_body)
|
|
||||||
.with_status(StatusCode::CREATED)
|
|
||||||
.into_response())
|
|
||||||
}
|
}
|
||||||
None => Ok(Response::new(tide::Body::empty())
|
None => Err(ResponseError::not_found("missing index schema")),
|
||||||
.with_status(StatusCode::NO_CONTENT)
|
|
||||||
.into_response()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_schema(mut ctx: Context<Data>) -> SResult<Response> {
|
pub async fn update_schema(mut ctx: Context<Data>) -> SResult<Response> {
|
||||||
ctx.is_allowed(IndexesWrite)?;
|
ctx.is_allowed(IndexesWrite)?;
|
||||||
|
|
||||||
let index_name = ctx.url_param("index")?;
|
let index_uid = ctx.url_param("index")?;
|
||||||
|
|
||||||
let schema = ctx
|
let params: SchemaParams = ctx.url_query().unwrap_or_default();
|
||||||
.body_json::<SchemaBody>()
|
|
||||||
.await
|
let schema = if params.raw {
|
||||||
.map_err(ResponseError::bad_request)?;
|
ctx.body_json::<SchemaBuilder>()
|
||||||
|
.await
|
||||||
|
.map_err(ResponseError::bad_request)?
|
||||||
|
.build()
|
||||||
|
} else {
|
||||||
|
ctx.body_json::<SchemaBody>()
|
||||||
|
.await
|
||||||
|
.map_err(ResponseError::bad_request)?
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
|
||||||
let db = &ctx.state().db;
|
let db = &ctx.state().db;
|
||||||
let env = &db.env;
|
let env = &db.env;
|
||||||
let mut writer = env.write_txn().map_err(ResponseError::internal)?;
|
let mut writer = env.write_txn().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
let index = db
|
let index = db
|
||||||
.open_index(&index_name)
|
.open_index(&index_uid)
|
||||||
.ok_or(ResponseError::index_not_found(index_name))?;
|
.ok_or(ResponseError::index_not_found(index_uid))?;
|
||||||
|
|
||||||
let schema: meilidb_schema::Schema = schema.into();
|
|
||||||
let update_id = index
|
let update_id = index
|
||||||
.schema_update(&mut writer, schema.clone())
|
.schema_update(&mut writer, schema.clone())
|
||||||
.map_err(ResponseError::internal)?;
|
.map_err(ResponseError::internal)?;
|
||||||
@ -169,12 +383,12 @@ pub async fn get_all_updates_status(ctx: Context<Data>) -> SResult<Response> {
|
|||||||
|
|
||||||
pub async fn delete_index(ctx: Context<Data>) -> SResult<StatusCode> {
|
pub async fn delete_index(ctx: Context<Data>) -> SResult<StatusCode> {
|
||||||
ctx.is_allowed(IndexesWrite)?;
|
ctx.is_allowed(IndexesWrite)?;
|
||||||
let index_name = ctx.url_param("index")?;
|
let index_uid = ctx.url_param("index")?;
|
||||||
|
|
||||||
let found = ctx
|
let found = ctx
|
||||||
.state()
|
.state()
|
||||||
.db
|
.db
|
||||||
.delete_index(&index_name)
|
.delete_index(&index_uid)
|
||||||
.map_err(ResponseError::internal)?;
|
.map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
@ -184,12 +398,35 @@ pub async fn delete_index(ctx: Context<Data>) -> SResult<StatusCode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_update_callback(index_name: &str, data: &Data, _status: ProcessedUpdateResult) {
|
pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpdateResult) {
|
||||||
let env = &data.db.env;
|
if status.error.is_some() {
|
||||||
let mut writer = env.write_txn().unwrap();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
data.compute_stats(&mut writer, &index_name).unwrap();
|
if let Some(index) = data.db.open_index(&index_uid) {
|
||||||
data.set_last_update(&mut writer, &index_name).unwrap();
|
let env = &data.db.env;
|
||||||
|
let mut writer = match env.write_txn() {
|
||||||
|
Ok(writer) => writer,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Impossible to get write_txn; {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
writer.commit().unwrap();
|
if let Err(e) = data.compute_stats(&mut writer, &index_uid) {
|
||||||
|
error!("Impossible to compute stats; {}", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = data.set_last_update(&mut writer) {
|
||||||
|
error!("Impossible to update last_update; {}", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = index.main.put_updated_at(&mut writer) {
|
||||||
|
error!("Impossible to update updated_at; {}", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = writer.commit() {
|
||||||
|
error!("Impossible to get write_txn; {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,8 @@ pub struct UpdatedRequest {
|
|||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
acl: Option<Vec<ACL>>,
|
acl: Option<Vec<ACL>>,
|
||||||
indexes: Option<Vec<Wildcard>>,
|
indexes: Option<Vec<Wildcard>>,
|
||||||
|
expires_at: Option<DateTime<Utc>>,
|
||||||
|
revoked: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update(mut ctx: Context<Data>) -> SResult<Response> {
|
pub async fn update(mut ctx: Context<Data>) -> SResult<Response> {
|
||||||
@ -154,6 +156,14 @@ pub async fn update(mut ctx: Context<Data>) -> SResult<Response> {
|
|||||||
token_config.indexes = indexes;
|
token_config.indexes = indexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(expires_at) = data.expires_at {
|
||||||
|
token_config.expires_at = expires_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(revoked) = data.revoked {
|
||||||
|
token_config.revoked = revoked;
|
||||||
|
}
|
||||||
|
|
||||||
token_config.updated_at = Utc::now();
|
token_config.updated_at = Utc::now();
|
||||||
|
|
||||||
common_store
|
common_store
|
||||||
@ -163,7 +173,7 @@ pub async fn update(mut ctx: Context<Data>) -> SResult<Response> {
|
|||||||
writer.commit().map_err(ResponseError::internal)?;
|
writer.commit().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
Ok(tide::response::json(token_config)
|
Ok(tide::response::json(token_config)
|
||||||
.with_status(StatusCode::ACCEPTED)
|
.with_status(StatusCode::OK)
|
||||||
.into_response())
|
.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,5 +195,5 @@ pub async fn delete(ctx: Context<Data>) -> SResult<StatusCode> {
|
|||||||
|
|
||||||
writer.commit().map_err(ResponseError::internal)?;
|
writer.commit().map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
Ok(StatusCode::ACCEPTED)
|
Ok(StatusCode::NO_CONTENT)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,10 @@ pub mod synonym;
|
|||||||
pub fn load_routes(app: &mut tide::App<Data>) {
|
pub fn load_routes(app: &mut tide::App<Data>) {
|
||||||
app.at("").nest(|router| {
|
app.at("").nest(|router| {
|
||||||
router.at("/indexes").nest(|router| {
|
router.at("/indexes").nest(|router| {
|
||||||
router.at("/").get(index::list_indexes);
|
router
|
||||||
|
.at("/")
|
||||||
|
.get(index::list_indexes)
|
||||||
|
.post(index::create_index);
|
||||||
|
|
||||||
router.at("/search").post(search::search_multi_index);
|
router.at("/search").post(search::search_multi_index);
|
||||||
|
|
||||||
@ -28,15 +31,19 @@ pub fn load_routes(app: &mut tide::App<Data>) {
|
|||||||
|
|
||||||
router
|
router
|
||||||
.at("/")
|
.at("/")
|
||||||
.get(index::get_index_schema)
|
.get(index::get_index)
|
||||||
.post(index::create_index)
|
.put(index::update_index)
|
||||||
.put(index::update_schema)
|
|
||||||
.delete(index::delete_index);
|
.delete(index::delete_index);
|
||||||
|
|
||||||
|
router
|
||||||
|
.at("/schema")
|
||||||
|
.get(index::get_index_schema)
|
||||||
|
.put(index::update_schema);
|
||||||
|
|
||||||
router.at("/documents").nest(|router| {
|
router.at("/documents").nest(|router| {
|
||||||
router
|
router
|
||||||
.at("/")
|
.at("/")
|
||||||
.get(document::browse_documents)
|
.get(document::get_all_documents)
|
||||||
.post(document::add_or_replace_multiple_documents)
|
.post(document::add_or_replace_multiple_documents)
|
||||||
.put(document::add_or_update_multiple_documents)
|
.put(document::add_or_update_multiple_documents)
|
||||||
.delete(document::clear_all_documents);
|
.delete(document::clear_all_documents);
|
||||||
@ -53,8 +60,12 @@ pub fn load_routes(app: &mut tide::App<Data>) {
|
|||||||
.post(document::delete_multiple_documents);
|
.post(document::delete_multiple_documents);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.at("/synonym").nest(|router| {
|
router.at("/synonyms").nest(|router| {
|
||||||
router.at("/").get(synonym::list).post(synonym::create);
|
router
|
||||||
|
.at("/")
|
||||||
|
.get(synonym::list)
|
||||||
|
.post(synonym::create)
|
||||||
|
.delete(synonym::clear);
|
||||||
|
|
||||||
router
|
router
|
||||||
.at("/:synonym")
|
.at("/:synonym")
|
||||||
@ -63,14 +74,13 @@ pub fn load_routes(app: &mut tide::App<Data>) {
|
|||||||
.delete(synonym::delete);
|
.delete(synonym::delete);
|
||||||
|
|
||||||
router.at("/batch").post(synonym::batch_write);
|
router.at("/batch").post(synonym::batch_write);
|
||||||
router.at("/clear").post(synonym::clear);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.at("/stop-words").nest(|router| {
|
router.at("/stop-words").nest(|router| {
|
||||||
router
|
router
|
||||||
.at("/")
|
.at("/")
|
||||||
.get(stop_words::list)
|
.get(stop_words::list)
|
||||||
.put(stop_words::add)
|
.patch(stop_words::add)
|
||||||
.delete(stop_words::delete);
|
.delete(stop_words::delete);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -155,13 +155,8 @@ pub async fn search_multi_index(mut ctx: Context<Data>) -> SResult<Response> {
|
|||||||
|
|
||||||
for index in index_list.clone() {
|
for index in index_list.clone() {
|
||||||
if index == "*" {
|
if index == "*" {
|
||||||
index_list = ctx
|
index_list = ctx.state().db.indexes_uids().into_iter().collect();
|
||||||
.state()
|
break;
|
||||||
.db
|
|
||||||
.indexes_names()
|
|
||||||
.map_err(ResponseError::internal)?
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,10 +176,10 @@ pub async fn search_multi_index(mut ctx: Context<Data>) -> SResult<Response> {
|
|||||||
let par_body = body.clone();
|
let par_body = body.clone();
|
||||||
let responses_per_index: Vec<SResult<_>> = index_list
|
let responses_per_index: Vec<SResult<_>> = index_list
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(move |index_name| {
|
.map(move |index_uid| {
|
||||||
let index: Index = db
|
let index: Index = db
|
||||||
.open_index(&index_name)
|
.open_index(&index_uid)
|
||||||
.ok_or(ResponseError::index_not_found(&index_name))?;
|
.ok_or(ResponseError::index_not_found(&index_uid))?;
|
||||||
|
|
||||||
let mut search_builder = index.new_search(par_body.query.clone());
|
let mut search_builder = index.new_search(par_body.query.clone());
|
||||||
|
|
||||||
@ -221,7 +216,7 @@ pub async fn search_multi_index(mut ctx: Context<Data>) -> SResult<Response> {
|
|||||||
let response = search_builder
|
let response = search_builder
|
||||||
.search(&reader)
|
.search(&reader)
|
||||||
.map_err(ResponseError::internal)?;
|
.map_err(ResponseError::internal)?;
|
||||||
Ok((index_name, response))
|
Ok((index_uid, response))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -230,11 +225,11 @@ pub async fn search_multi_index(mut ctx: Context<Data>) -> SResult<Response> {
|
|||||||
let mut max_query_time = 0;
|
let mut max_query_time = 0;
|
||||||
|
|
||||||
for response in responses_per_index {
|
for response in responses_per_index {
|
||||||
if let Ok((index_name, response)) = response {
|
if let Ok((index_uid, response)) = response {
|
||||||
if response.processing_time_ms > max_query_time {
|
if response.processing_time_ms > max_query_time {
|
||||||
max_query_time = response.processing_time_ms;
|
max_query_time = response.processing_time_ms;
|
||||||
}
|
}
|
||||||
hits_map.insert(index_name, response.hits);
|
hits_map.insert(index_uid, response.hits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use log::error;
|
||||||
use pretty_bytes::converter::convert;
|
use pretty_bytes::converter::convert;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sysinfo::{NetworkExt, Pid, ProcessExt, ProcessorExt, System, SystemExt};
|
use sysinfo::{NetworkExt, Pid, ProcessExt, ProcessorExt, System, SystemExt};
|
||||||
@ -17,13 +18,12 @@ use crate::Data;
|
|||||||
struct IndexStatsResponse {
|
struct IndexStatsResponse {
|
||||||
number_of_documents: u64,
|
number_of_documents: u64,
|
||||||
is_indexing: bool,
|
is_indexing: bool,
|
||||||
last_update: Option<DateTime<Utc>>,
|
|
||||||
fields_frequency: HashMap<String, usize>,
|
fields_frequency: HashMap<String, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn index_stat(ctx: Context<Data>) -> SResult<Response> {
|
pub async fn index_stat(ctx: Context<Data>) -> SResult<Response> {
|
||||||
ctx.is_allowed(Admin)?;
|
ctx.is_allowed(Admin)?;
|
||||||
let index_name = ctx.url_param("index")?;
|
let index_uid = ctx.url_param("index")?;
|
||||||
let index = ctx.index()?;
|
let index = ctx.index()?;
|
||||||
|
|
||||||
let env = &ctx.state().db.env;
|
let env = &ctx.state().db.env;
|
||||||
@ -34,27 +34,21 @@ pub async fn index_stat(ctx: Context<Data>) -> SResult<Response> {
|
|||||||
.number_of_documents(&reader)
|
.number_of_documents(&reader)
|
||||||
.map_err(ResponseError::internal)?;
|
.map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
let fields_frequency = ctx
|
let fields_frequency = index
|
||||||
.state()
|
.main
|
||||||
.fields_frequency(&reader, &index_name)
|
.fields_frequency(&reader)
|
||||||
.map_err(ResponseError::internal)?
|
.map_err(ResponseError::internal)?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let is_indexing = ctx
|
let is_indexing = ctx
|
||||||
.state()
|
.state()
|
||||||
.is_indexing(&reader, &index_name)
|
.is_indexing(&reader, &index_uid)
|
||||||
.map_err(ResponseError::internal)?
|
.map_err(ResponseError::internal)?
|
||||||
.ok_or(ResponseError::not_found("Index not found"))?;
|
.ok_or(ResponseError::internal("'is_indexing' date not found"))?;
|
||||||
|
|
||||||
let last_update = ctx
|
|
||||||
.state()
|
|
||||||
.last_update(&reader, &index_name)
|
|
||||||
.map_err(ResponseError::internal)?;
|
|
||||||
|
|
||||||
let response = IndexStatsResponse {
|
let response = IndexStatsResponse {
|
||||||
number_of_documents,
|
number_of_documents,
|
||||||
is_indexing,
|
is_indexing,
|
||||||
last_update,
|
|
||||||
fields_frequency,
|
fields_frequency,
|
||||||
};
|
};
|
||||||
Ok(tide::response::json(response))
|
Ok(tide::response::json(response))
|
||||||
@ -64,6 +58,7 @@ pub async fn index_stat(ctx: Context<Data>) -> SResult<Response> {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct StatsResult {
|
struct StatsResult {
|
||||||
database_size: u64,
|
database_size: u64,
|
||||||
|
last_update: Option<DateTime<Utc>>,
|
||||||
indexes: HashMap<String, IndexStatsResponse>,
|
indexes: HashMap<String, IndexStatsResponse>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,43 +67,44 @@ pub async fn get_stats(ctx: Context<Data>) -> SResult<Response> {
|
|||||||
|
|
||||||
let mut index_list = HashMap::new();
|
let mut index_list = HashMap::new();
|
||||||
|
|
||||||
if let Ok(indexes_set) = ctx.state().db.indexes_names() {
|
let db = &ctx.state().db;
|
||||||
for index_name in indexes_set {
|
let env = &db.env;
|
||||||
let db = &ctx.state().db;
|
let reader = env.read_txn().map_err(ResponseError::internal)?;
|
||||||
let env = &db.env;
|
|
||||||
|
|
||||||
let index = db.open_index(&index_name).unwrap();
|
let indexes_set = ctx.state().db.indexes_uids();
|
||||||
let reader = env.read_txn().map_err(ResponseError::internal)?;
|
for index_uid in indexes_set {
|
||||||
|
let index = ctx.state().db.open_index(&index_uid);
|
||||||
|
|
||||||
let number_of_documents = index
|
match index {
|
||||||
.main
|
Some(index) => {
|
||||||
.number_of_documents(&reader)
|
let number_of_documents = index
|
||||||
.map_err(ResponseError::internal)?;
|
.main
|
||||||
|
.number_of_documents(&reader)
|
||||||
|
.map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
let fields_frequency = ctx
|
let fields_frequency = index
|
||||||
.state()
|
.main
|
||||||
.fields_frequency(&reader, &index_name)
|
.fields_frequency(&reader)
|
||||||
.map_err(ResponseError::internal)?
|
.map_err(ResponseError::internal)?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let is_indexing = ctx
|
let is_indexing = ctx
|
||||||
.state()
|
.state()
|
||||||
.is_indexing(&reader, &index_name)
|
.is_indexing(&reader, &index_uid)
|
||||||
.map_err(ResponseError::internal)?
|
.map_err(ResponseError::internal)?
|
||||||
.ok_or(ResponseError::not_found("Index not found"))?;
|
.ok_or(ResponseError::internal("'is_indexing' date not found"))?;
|
||||||
|
|
||||||
let last_update = ctx
|
let response = IndexStatsResponse {
|
||||||
.state()
|
number_of_documents,
|
||||||
.last_update(&reader, &index_name)
|
is_indexing,
|
||||||
.map_err(ResponseError::internal)?;
|
fields_frequency,
|
||||||
|
};
|
||||||
let response = IndexStatsResponse {
|
index_list.insert(index_uid, response);
|
||||||
number_of_documents,
|
}
|
||||||
is_indexing,
|
None => error!(
|
||||||
last_update,
|
"Index {:?} is referenced in the indexes list but cannot be found",
|
||||||
fields_frequency,
|
index_uid
|
||||||
};
|
),
|
||||||
index_list.insert(index_name, response);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,8 +115,14 @@ pub async fn get_stats(ctx: Context<Data>) -> SResult<Response> {
|
|||||||
.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 = ctx
|
||||||
|
.state()
|
||||||
|
.last_update(&reader)
|
||||||
|
.map_err(ResponseError::internal)?;
|
||||||
|
|
||||||
let response = StatsResult {
|
let response = StatsResult {
|
||||||
database_size,
|
database_size,
|
||||||
|
last_update,
|
||||||
indexes: index_list,
|
indexes: index_list,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ pub async fn create(mut ctx: Context<Data>) -> SResult<Response> {
|
|||||||
|
|
||||||
let response_body = IndexUpdateResponse { update_id };
|
let response_body = IndexUpdateResponse { update_id };
|
||||||
Ok(tide::response::json(response_body)
|
Ok(tide::response::json(response_body)
|
||||||
.with_status(StatusCode::CREATED)
|
.with_status(StatusCode::ACCEPTED)
|
||||||
.into_response())
|
.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user