2021-05-31 22:40:59 +08:00
|
|
|
use std::collections::{BTreeSet, HashSet};
|
|
|
|
use std::fs::create_dir_all;
|
|
|
|
use std::marker::PhantomData;
|
2021-03-04 18:56:32 +08:00
|
|
|
use std::ops::Deref;
|
2021-05-31 22:40:59 +08:00
|
|
|
use std::path::Path;
|
2021-03-15 17:17:41 +08:00
|
|
|
use std::sync::Arc;
|
2021-03-04 18:56:32 +08:00
|
|
|
|
2021-05-27 04:52:06 +08:00
|
|
|
use heed::{EnvOpenOptions, RoTxn};
|
2021-08-25 02:55:29 +08:00
|
|
|
use milli::update::Setting;
|
2021-07-28 18:20:13 +08:00
|
|
|
use milli::{obkv_to_json, FieldId};
|
2021-03-15 17:17:41 +08:00
|
|
|
use serde_json::{Map, Value};
|
2021-03-04 21:20:19 +08:00
|
|
|
|
2021-06-15 03:26:35 +08:00
|
|
|
use error::Result;
|
2021-06-23 20:48:33 +08:00
|
|
|
pub use search::{default_crop_length, SearchQuery, SearchResult, DEFAULT_SEARCH_LIMIT};
|
2021-05-31 22:03:39 +08:00
|
|
|
pub use updates::{Checked, Facets, Settings, Unchecked};
|
2021-03-04 18:56:32 +08:00
|
|
|
|
2021-08-25 02:55:29 +08:00
|
|
|
use crate::helpers::EnvSizer;
|
|
|
|
|
2021-06-15 03:26:35 +08:00
|
|
|
use self::error::IndexError;
|
|
|
|
|
|
|
|
pub mod error;
|
|
|
|
pub mod update_handler;
|
|
|
|
|
2021-05-27 04:52:06 +08:00
|
|
|
mod dump;
|
2021-05-31 22:03:39 +08:00
|
|
|
mod search;
|
|
|
|
mod updates;
|
2021-04-01 22:44:42 +08:00
|
|
|
|
2021-03-04 21:20:19 +08:00
|
|
|
pub type Document = Map<String, Value>;
|
|
|
|
|
2021-03-04 18:56:32 +08:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Index(pub Arc<milli::Index>);
|
|
|
|
|
|
|
|
impl Deref for Index {
|
|
|
|
type Target = milli::Index;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
self.0.as_ref()
|
|
|
|
}
|
|
|
|
}
|
2021-03-04 19:38:55 +08:00
|
|
|
|
|
|
|
impl Index {
|
2021-06-15 03:26:35 +08:00
|
|
|
pub fn open(path: impl AsRef<Path>, size: usize) -> Result<Self> {
|
2021-05-31 22:40:59 +08:00
|
|
|
create_dir_all(&path)?;
|
2021-05-27 04:52:06 +08:00
|
|
|
let mut options = EnvOpenOptions::new();
|
|
|
|
options.map_size(size);
|
2021-06-17 20:36:32 +08:00
|
|
|
let index = milli::Index::new(options, &path)?;
|
2021-05-27 04:52:06 +08:00
|
|
|
Ok(Index(Arc::new(index)))
|
|
|
|
}
|
|
|
|
|
2021-06-15 03:26:35 +08:00
|
|
|
pub fn settings(&self) -> Result<Settings<Checked>> {
|
2021-03-04 19:38:55 +08:00
|
|
|
let txn = self.read_txn()?;
|
2021-05-25 00:16:35 +08:00
|
|
|
self.settings_txn(&txn)
|
|
|
|
}
|
2021-03-04 19:38:55 +08:00
|
|
|
|
2021-06-15 03:26:35 +08:00
|
|
|
pub fn settings_txn(&self, txn: &RoTxn) -> Result<Settings<Checked>> {
|
2021-03-04 19:38:55 +08:00
|
|
|
let displayed_attributes = self
|
2021-07-30 00:14:36 +08:00
|
|
|
.displayed_fields(txn)?
|
2021-05-11 17:47:04 +08:00
|
|
|
.map(|fields| fields.into_iter().map(String::from).collect());
|
2021-03-04 19:38:55 +08:00
|
|
|
|
|
|
|
let searchable_attributes = self
|
2021-07-30 00:14:36 +08:00
|
|
|
.searchable_fields(txn)?
|
2021-05-11 17:47:04 +08:00
|
|
|
.map(|fields| fields.into_iter().map(String::from).collect());
|
2021-03-04 19:38:55 +08:00
|
|
|
|
2021-07-30 00:14:36 +08:00
|
|
|
let filterable_attributes = self.filterable_fields(txn)?.into_iter().collect();
|
2021-03-04 19:38:55 +08:00
|
|
|
|
2021-03-12 05:39:16 +08:00
|
|
|
let criteria = self
|
2021-07-30 00:14:36 +08:00
|
|
|
.criteria(txn)?
|
2021-03-12 05:39:16 +08:00
|
|
|
.into_iter()
|
|
|
|
.map(|c| c.to_string())
|
|
|
|
.collect();
|
|
|
|
|
2021-04-06 21:41:03 +08:00
|
|
|
let stop_words = self
|
2021-07-30 00:14:36 +08:00
|
|
|
.stop_words(txn)?
|
2021-06-15 03:26:35 +08:00
|
|
|
.map(|stop_words| -> Result<BTreeSet<_>> {
|
2021-04-09 20:41:24 +08:00
|
|
|
Ok(stop_words.stream().into_strs()?.into_iter().collect())
|
2021-04-06 21:41:03 +08:00
|
|
|
})
|
|
|
|
.transpose()?
|
|
|
|
.unwrap_or_else(BTreeSet::new);
|
2021-07-30 00:14:36 +08:00
|
|
|
let distinct_field = self.distinct_field(txn)?.map(String::from);
|
2021-04-06 21:41:03 +08:00
|
|
|
|
2021-06-14 16:38:56 +08:00
|
|
|
// in milli each word in the synonyms map were split on their separator. Since we lost
|
|
|
|
// this information we are going to put space between words.
|
2021-06-03 20:19:56 +08:00
|
|
|
let synonyms = self
|
2021-07-30 00:14:36 +08:00
|
|
|
.synonyms(txn)?
|
2021-06-03 20:19:56 +08:00
|
|
|
.iter()
|
|
|
|
.map(|(key, values)| {
|
|
|
|
(
|
|
|
|
key.join(" "),
|
|
|
|
values.iter().map(|value| value.join(" ")).collect(),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2021-03-04 19:38:55 +08:00
|
|
|
Ok(Settings {
|
2021-08-25 02:55:29 +08:00
|
|
|
displayed_attributes: match displayed_attributes {
|
|
|
|
Some(attrs) => Setting::Set(attrs),
|
|
|
|
None => Setting::Reset,
|
|
|
|
},
|
|
|
|
searchable_attributes: match searchable_attributes {
|
|
|
|
Some(attrs) => Setting::Set(attrs),
|
|
|
|
None => Setting::Reset,
|
|
|
|
},
|
|
|
|
filterable_attributes: Setting::Set(filterable_attributes),
|
|
|
|
ranking_rules: Setting::Set(criteria),
|
|
|
|
stop_words: Setting::Set(stop_words),
|
|
|
|
distinct_attribute: match distinct_field {
|
|
|
|
Some(field) => Setting::Set(field),
|
|
|
|
None => Setting::Reset,
|
|
|
|
},
|
|
|
|
synonyms: Setting::Set(synonyms),
|
2021-05-10 23:30:09 +08:00
|
|
|
_kind: PhantomData,
|
2021-03-04 19:38:55 +08:00
|
|
|
})
|
|
|
|
}
|
2021-03-04 21:20:19 +08:00
|
|
|
|
2021-03-16 01:11:10 +08:00
|
|
|
pub fn retrieve_documents<S: AsRef<str>>(
|
2021-03-04 21:20:19 +08:00
|
|
|
&self,
|
|
|
|
offset: usize,
|
|
|
|
limit: usize,
|
|
|
|
attributes_to_retrieve: Option<Vec<S>>,
|
2021-06-15 03:26:35 +08:00
|
|
|
) -> Result<Vec<Map<String, Value>>> {
|
2021-03-04 21:20:19 +08:00
|
|
|
let txn = self.read_txn()?;
|
|
|
|
|
|
|
|
let fields_ids_map = self.fields_ids_map(&txn)?;
|
2021-06-17 20:36:32 +08:00
|
|
|
let fields_to_display =
|
|
|
|
self.fields_to_display(&txn, &attributes_to_retrieve, &fields_ids_map)?;
|
2021-03-04 21:20:19 +08:00
|
|
|
|
|
|
|
let iter = self.documents.range(&txn, &(..))?.skip(offset).take(limit);
|
|
|
|
|
|
|
|
let mut documents = Vec::new();
|
|
|
|
|
|
|
|
for entry in iter {
|
|
|
|
let (_id, obkv) = entry?;
|
2021-06-17 20:36:32 +08:00
|
|
|
let object = obkv_to_json(&fields_to_display, &fields_ids_map, obkv)?;
|
2021-03-04 21:20:19 +08:00
|
|
|
documents.push(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(documents)
|
|
|
|
}
|
2021-03-04 22:09:00 +08:00
|
|
|
|
|
|
|
pub fn retrieve_document<S: AsRef<str>>(
|
|
|
|
&self,
|
|
|
|
doc_id: String,
|
|
|
|
attributes_to_retrieve: Option<Vec<S>>,
|
2021-06-15 03:26:35 +08:00
|
|
|
) -> Result<Map<String, Value>> {
|
2021-03-15 17:17:41 +08:00
|
|
|
let txn = self.read_txn()?;
|
2021-03-04 22:09:00 +08:00
|
|
|
|
2021-03-15 17:17:41 +08:00
|
|
|
let fields_ids_map = self.fields_ids_map(&txn)?;
|
2021-03-04 22:09:00 +08:00
|
|
|
|
2021-06-17 20:36:32 +08:00
|
|
|
let fields_to_display =
|
|
|
|
self.fields_to_display(&txn, &attributes_to_retrieve, &fields_ids_map)?;
|
2021-03-15 17:36:12 +08:00
|
|
|
|
2021-03-15 17:17:41 +08:00
|
|
|
let internal_id = self
|
2021-06-17 20:36:32 +08:00
|
|
|
.external_documents_ids(&txn)?
|
2021-03-15 17:17:41 +08:00
|
|
|
.get(doc_id.as_bytes())
|
2021-06-15 03:26:35 +08:00
|
|
|
.ok_or_else(|| IndexError::DocumentNotFound(doc_id.clone()))?;
|
2021-03-15 17:17:41 +08:00
|
|
|
|
|
|
|
let document = self
|
2021-06-17 20:36:32 +08:00
|
|
|
.documents(&txn, std::iter::once(internal_id))?
|
2021-03-15 17:17:41 +08:00
|
|
|
.into_iter()
|
|
|
|
.next()
|
2021-06-17 20:36:32 +08:00
|
|
|
.map(|(_, d)| d)
|
|
|
|
.ok_or(IndexError::DocumentNotFound(doc_id))?;
|
|
|
|
|
|
|
|
let document = obkv_to_json(&fields_to_display, &fields_ids_map, document)?;
|
|
|
|
|
|
|
|
Ok(document)
|
2021-03-04 22:09:00 +08:00
|
|
|
}
|
2021-03-15 18:01:14 +08:00
|
|
|
|
2021-04-09 20:41:24 +08:00
|
|
|
pub fn size(&self) -> u64 {
|
|
|
|
self.env.size()
|
2021-04-01 22:44:42 +08:00
|
|
|
}
|
|
|
|
|
2021-03-15 18:01:14 +08:00
|
|
|
fn fields_to_display<S: AsRef<str>>(
|
|
|
|
&self,
|
|
|
|
txn: &heed::RoTxn,
|
2021-04-19 22:22:41 +08:00
|
|
|
attributes_to_retrieve: &Option<Vec<S>>,
|
2021-03-15 18:01:14 +08:00
|
|
|
fields_ids_map: &milli::FieldsIdsMap,
|
2021-07-28 18:08:19 +08:00
|
|
|
) -> Result<Vec<FieldId>> {
|
2021-07-30 00:14:36 +08:00
|
|
|
let mut displayed_fields_ids = match self.displayed_fields_ids(txn)? {
|
2021-03-15 18:01:14 +08:00
|
|
|
Some(ids) => ids.into_iter().collect::<Vec<_>>(),
|
|
|
|
None => fields_ids_map.iter().map(|(id, _)| id).collect(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let attributes_to_retrieve_ids = match attributes_to_retrieve {
|
|
|
|
Some(attrs) => attrs
|
|
|
|
.iter()
|
|
|
|
.filter_map(|f| fields_ids_map.id(f.as_ref()))
|
|
|
|
.collect::<HashSet<_>>(),
|
|
|
|
None => fields_ids_map.iter().map(|(id, _)| id).collect(),
|
|
|
|
};
|
|
|
|
|
|
|
|
displayed_fields_ids.retain(|fid| attributes_to_retrieve_ids.contains(fid));
|
|
|
|
Ok(displayed_fields_ids)
|
|
|
|
}
|
2021-03-04 19:38:55 +08:00
|
|
|
}
|