From 3b64735058c678a8314bf118a92a84c9ed47d11d Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Mon, 28 Dec 2020 19:08:53 +0100 Subject: [PATCH 01/32] Introduce a struct to compute facets values --- http-ui/public/script.js | 6 +- http-ui/src/main.rs | 23 +++++- src/index.rs | 6 +- src/lib.rs | 2 +- src/search/facet/facet_distribution.rs | 106 +++++++++++++++++++++++++ src/search/facet/mod.rs | 10 ++- src/search/mod.rs | 23 +++--- 7 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 src/search/facet/facet_distribution.rs diff --git a/http-ui/public/script.js b/http-ui/public/script.js index fb7a95cc9..a2005a9bd 100644 --- a/http-ui/public/script.js +++ b/http-ui/public/script.js @@ -15,18 +15,18 @@ $('#query, #facet').on('input', function () { type: "POST", url: "query", contentType: 'application/json', - data: JSON.stringify({ 'query': query, 'facetCondition': facet }), + data: JSON.stringify({ 'query': query, 'facetCondition': facet, "facetDistribution": true }), contentType: 'application/json', success: function (data, textStatus, request) { results.innerHTML = ''; let timeSpent = request.getResponseHeader('Time-Ms'); - let numberOfDocuments = data.length; + let numberOfDocuments = data.documents.length; count.innerHTML = `${numberOfDocuments}`; time.innerHTML = `${timeSpent}ms`; time.classList.remove('fade-in-out'); - for (element of data) { + for (element of data.documents) { const elem = document.createElement('li'); elem.classList.add("document"); diff --git a/http-ui/src/main.rs b/http-ui/src/main.rs index 366dbaa1e..fe2f71c8e 100644 --- a/http-ui/src/main.rs +++ b/http-ui/src/main.rs @@ -626,6 +626,14 @@ async fn main() -> anyhow::Result<()> { struct QueryBody { query: Option, facet_condition: Option, + facet_distribution: Option, + } + + #[derive(Debug, Serialize)] + #[serde(rename_all = "camelCase")] + struct Answer { + documents: Vec>, + facets: HashMap>, } let disable_highlighting = opt.disable_highlighting; @@ -649,7 +657,13 @@ async fn main() -> anyhow::Result<()> { } } - let SearchResult { found_words, documents_ids } = search.execute().unwrap(); + let SearchResult { found_words, candidates, documents_ids } = search.execute().unwrap(); + + let facets = if query.facet_distribution == Some(true) { + Some(index.facets(&rtxn).candidates(candidates).execute().unwrap()) + } else { + None + }; let mut documents = Vec::new(); let fields_ids_map = index.fields_ids_map(&rtxn).unwrap(); @@ -674,10 +688,15 @@ async fn main() -> anyhow::Result<()> { documents.push(object); } + let answer = Answer { + documents, + facets: facets.unwrap_or_default(), + }; + Response::builder() .header("Content-Type", "application/json") .header("Time-Ms", before_search.elapsed().as_millis().to_string()) - .body(serde_json::to_string(&documents).unwrap()) + .body(serde_json::to_string(&answer).unwrap()) }); let index_cloned = index.clone(); diff --git a/src/index.rs b/src/index.rs index 601816148..6020e332c 100644 --- a/src/index.rs +++ b/src/index.rs @@ -9,7 +9,7 @@ use roaring::RoaringBitmap; use crate::facet::FacetType; use crate::fields_ids_map::FieldsIdsMap; -use crate::{default_criteria, Criterion, Search}; +use crate::{default_criteria, Criterion, Search, FacetDistribution}; use crate::{BEU32, DocumentId, FieldId, ExternalDocumentsIds}; use crate::{ RoaringBitmapCodec, BEU32StrCodec, StrStrU8Codec, ObkvCodec, @@ -351,6 +351,10 @@ impl Index { Ok(self.documents_ids(rtxn).map(|docids| docids.len() as usize)?) } + pub fn facets<'a>(&'a self, rtxn: &'a RoTxn) -> FacetDistribution<'a> { + FacetDistribution::new(rtxn, self) + } + pub fn search<'a>(&'a self, rtxn: &'a RoTxn) -> Search<'a> { Search::new(rtxn, self) } diff --git a/src/lib.rs b/src/lib.rs index 435c3be91..09a66ea65 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ pub use self::fields_ids_map::FieldsIdsMap; pub use self::heed_codec::{BEU32StrCodec, StrStrU8Codec, ObkvCodec}; pub use self::heed_codec::{RoaringBitmapCodec, BoRoaringBitmapCodec, CboRoaringBitmapCodec}; pub use self::index::Index; -pub use self::search::{Search, FacetCondition, SearchResult}; +pub use self::search::{Search, FacetDistribution, FacetCondition, SearchResult}; pub use self::update_store::UpdateStore; pub type FastMap4 = HashMap>; diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs new file mode 100644 index 000000000..3af475c71 --- /dev/null +++ b/src/search/facet/facet_distribution.rs @@ -0,0 +1,106 @@ +use std::collections::{HashSet, HashMap}; +use std::fmt; +use std::ops::Bound::Unbounded; + +use roaring::RoaringBitmap; +use serde_json::Value; + +use crate::facet::FacetType; +use crate::heed_codec::facet::{FacetValueStringCodec, FacetLevelValueF64Codec, FacetLevelValueI64Codec}; +use crate::search::facet::FacetRange; +use crate::{Index, FieldId}; + +pub struct FacetDistribution<'a> { + facets: Option>, + candidates: Option, + rtxn: &'a heed::RoTxn<'a>, + index: &'a Index, +} + +impl<'a> FacetDistribution<'a> { + pub fn new(rtxn: &'a heed::RoTxn, index: &'a Index) -> FacetDistribution<'a> { + FacetDistribution { facets: None, candidates: None, rtxn, index } + } + + pub fn candidates(&mut self, candidates: RoaringBitmap) -> &mut Self { + self.candidates = Some(candidates); + self + } + + pub fn facets, A: AsRef>(&mut self, names: I) -> &mut Self { + self.facets = Some(names.into_iter().map(|s| s.as_ref().to_string()).collect()); + self + } + + fn facet_values(&self, field_id: FieldId, field_type: FacetType) -> heed::Result> { + let db = self.index.facet_field_id_value_docids; + let iter = match field_type { + FacetType::String => { + let iter = db + .prefix_iter(&self.rtxn, &[field_id])? + .remap_key_type::() + .map(|r| r.map(|((_, v), docids)| (Value::from(v), docids))); + Box::new(iter) as Box::> + }, + FacetType::Integer => { + let db = db.remap_key_type::(); + let range = FacetRange::::new( + self.rtxn, db, field_id, 0, Unbounded, Unbounded, + )?; + Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (Value::from(v), docids)))) + }, + FacetType::Float => { + let db = db.remap_key_type::(); + let range = FacetRange::::new( + self.rtxn, db, field_id, 0, Unbounded, Unbounded, + )?; + Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (Value::from(v), docids)))) + }, + }; + + let mut facet_values = Vec::new(); + for result in iter { + let (value, docids) = result?; + match &self.candidates { + Some(candidates) => if !docids.is_disjoint(candidates) { + facet_values.push(value); + }, + None => facet_values.push(value), + } + } + Ok(facet_values) + } + + pub fn execute(&self) -> heed::Result>> { + let fields_ids_map = self.index.fields_ids_map(self.rtxn)?; + let faceted_fields = self.index.faceted_fields(self.rtxn)?; + let fields_ids: Vec<_> = match &self.facets { + Some(names) => { + names.iter().filter_map(|n| { + let id = fields_ids_map.id(n)?; + faceted_fields.get(&id).cloned().map(|t| (id, t)) + }).collect() + }, + None => faceted_fields.iter().map(|(id, t)| (*id, *t)).collect(), + }; + + let mut facets_values = HashMap::new(); + for (fid, ftype) in fields_ids { + let facet_name = fields_ids_map.name(fid).unwrap(); + let values = self.facet_values(fid, ftype)?; + facets_values.insert(facet_name.to_string(), values); + } + + Ok(facets_values) + } +} + +impl fmt::Debug for FacetDistribution<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let FacetDistribution { facets, candidates, rtxn: _, index: _ } = self; + f.debug_struct("FacetDistribution") + .field("facets", facets) + .field("candidates", candidates) + .finish() + } +} diff --git a/src/search/facet/mod.rs b/src/search/facet/mod.rs index 41212e83e..70b5b4658 100644 --- a/src/search/facet/mod.rs +++ b/src/search/facet/mod.rs @@ -13,11 +13,13 @@ use crate::heed_codec::CboRoaringBitmapCodec; use crate::{Index, FieldId}; pub use self::facet_condition::{FacetCondition, FacetNumberOperator, FacetStringOperator}; +pub use self::facet_distribution::FacetDistribution; mod facet_condition; +mod facet_distribution; mod parser; -struct FacetRange<'t, T: 't, KC> { +pub struct FacetRange<'t, T: 't, KC> { iter: RoRange<'t, KC, LazyDecode>, end: Bound, } @@ -27,7 +29,7 @@ where KC: for<'a> BytesEncode<'a, EItem = (FieldId, u8, T, T)>, T: PartialOrd + Copy + Bounded, { - fn new( + pub fn new( rtxn: &'t heed::RoTxn, db: Database, field_id: FieldId, @@ -78,7 +80,7 @@ where } } -struct FacetRevRange<'t, T: 't, KC> { +pub struct FacetRevRange<'t, T: 't, KC> { iter: RoRevRange<'t, KC, LazyDecode>, end: Bound, } @@ -88,7 +90,7 @@ where KC: for<'a> BytesEncode<'a, EItem = (FieldId, u8, T, T)>, T: PartialOrd + Copy + Bounded, { - fn new( + pub fn new( rtxn: &'t heed::RoTxn, db: Database, field_id: FieldId, diff --git a/src/search/mod.rs b/src/search/mod.rs index 45fd0d709..05999caed 100644 --- a/src/search/mod.rs +++ b/src/search/mod.rs @@ -20,7 +20,7 @@ use crate::mdfs::Mdfs; use crate::query_tokens::{query_tokens, QueryToken}; use crate::{Index, FieldId, DocumentId, Criterion}; -pub use self::facet::{FacetCondition, FacetNumberOperator, FacetStringOperator}; +pub use self::facet::{FacetCondition, FacetDistribution, FacetNumberOperator, FacetStringOperator}; pub use self::facet::{FacetIter}; // Building these factories is not free. @@ -313,22 +313,26 @@ impl<'a> Search<'a> { // there is some facet conditions we return a placeholder. let documents_ids = match order_by_facet { Some((fid, ftype, is_ascending)) => { - self.facet_ordered(fid, ftype, is_ascending, facet_candidates, limit)? + self.facet_ordered(fid, ftype, is_ascending, facet_candidates.clone(), limit)? }, None => facet_candidates.iter().take(limit).collect(), }; - return Ok(SearchResult { documents_ids, ..Default::default() }) + return Ok(SearchResult { + documents_ids, + candidates: facet_candidates, + ..Default::default() + }) }, (None, None) => { // If the query is not set or results in no DFAs we return a placeholder. - let documents_ids = self.index.documents_ids(self.rtxn)?; + let all_docids = self.index.documents_ids(self.rtxn)?; let documents_ids = match order_by_facet { Some((fid, ftype, is_ascending)) => { - self.facet_ordered(fid, ftype, is_ascending, documents_ids, limit)? + self.facet_ordered(fid, ftype, is_ascending, all_docids.clone(), limit)? }, - None => documents_ids.iter().take(limit).collect(), + None => all_docids.iter().take(limit).collect(), }; - return Ok(SearchResult { documents_ids, ..Default::default() }) + return Ok(SearchResult { documents_ids, candidates: all_docids,..Default::default() }) }, }; @@ -336,7 +340,7 @@ impl<'a> Search<'a> { // The mana depth first search is a revised DFS that explore // solutions in the order of their proximities. - let mut mdfs = Mdfs::new(self.index, self.rtxn, &derived_words, candidates); + let mut mdfs = Mdfs::new(self.index, self.rtxn, &derived_words, candidates.clone()); let mut documents = Vec::new(); // We execute the Mdfs iterator until we find enough documents. @@ -364,7 +368,7 @@ impl<'a> Search<'a> { None => documents.into_iter().flatten().take(limit).collect(), }; - Ok(SearchResult { found_words, documents_ids }) + Ok(SearchResult { found_words, candidates, documents_ids }) } } @@ -383,6 +387,7 @@ impl fmt::Debug for Search<'_> { #[derive(Default)] pub struct SearchResult { pub found_words: HashSet, + pub candidates: RoaringBitmap, // TODO those documents ids should be associated with their criteria scores. pub documents_ids: Vec, } From d25a859985dd048ce1e896b9c51dea9bc288f622 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Mon, 28 Dec 2020 19:32:56 +0100 Subject: [PATCH 02/32] Display the facet values on the HTML debug page --- http-ui/public/script.js | 15 ++++++++++++++- http-ui/public/style.css | 12 ++++++++++++ http-ui/templates/index.html | 4 ++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/http-ui/public/script.js b/http-ui/public/script.js index a2005a9bd..b73bd0cd5 100644 --- a/http-ui/public/script.js +++ b/http-ui/public/script.js @@ -4,6 +4,7 @@ var timeoutID = null; $('#query, #facet').on('input', function () { var query = $('#query').val(); var facet = $('#facet').val(); + let fetchFacetDistribution = query.trim() !== "" || facet.trim() !== ""; var timeoutMs = 100; if (timeoutID !== null) { @@ -15,10 +16,13 @@ $('#query, #facet').on('input', function () { type: "POST", url: "query", contentType: 'application/json', - data: JSON.stringify({ 'query': query, 'facetCondition': facet, "facetDistribution": true }), + data: JSON.stringify({ + 'query': query, 'facetCondition': facet, "facetDistribution": fetchFacetDistribution + }), contentType: 'application/json', success: function (data, textStatus, request) { results.innerHTML = ''; + facets.innerHTML = ''; let timeSpent = request.getResponseHeader('Time-Ms'); let numberOfDocuments = data.documents.length; @@ -26,6 +30,15 @@ $('#query, #facet').on('input', function () { time.innerHTML = `${timeSpent}ms`; time.classList.remove('fade-in-out'); + for (facet_name in data.facets) { + for (value of data.facets[facet_name]) { + const elem = document.createElement('span'); + elem.classList.add("tag"); + elem.innerHTML = `${facet_name}:${value}`; + facets.appendChild(elem); + } + } + for (element of data.documents) { const elem = document.createElement('li'); elem.classList.add("document"); diff --git a/http-ui/public/style.css b/http-ui/public/style.css index 970b71ec5..2e58c24ff 100644 --- a/http-ui/public/style.css +++ b/http-ui/public/style.css @@ -4,6 +4,18 @@ padding: 0; } +#facets .tag { + margin-right: 1em; + margin-bottom: 1em; +} +#facets { + max-width: 900px; + margin: 20px auto 0 auto; + padding: 0; + max-height: 16em; + overflow: scroll; +} + #logo-white { display: none; } diff --git a/http-ui/templates/index.html b/http-ui/templates/index.html index 0ef239622..49d36bb65 100644 --- a/http-ui/templates/index.html +++ b/http-ui/templates/index.html @@ -84,6 +84,10 @@ +
+ +
+
    From 510df4729c471c65457a99fe96645c0232b4b23a Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 29 Dec 2020 00:44:42 +0100 Subject: [PATCH 03/32] Append the facet value to the facet query on click --- http-ui/public/script.js | 19 +++++++++++++++++-- http-ui/public/style.css | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/http-ui/public/script.js b/http-ui/public/script.js index b73bd0cd5..58fc74eab 100644 --- a/http-ui/public/script.js +++ b/http-ui/public/script.js @@ -34,6 +34,8 @@ $('#query, #facet').on('input', function () { for (value of data.facets[facet_name]) { const elem = document.createElement('span'); elem.classList.add("tag"); + elem.setAttribute('data-name', facet_name); + elem.setAttribute('data-value', value); elem.innerHTML = `${facet_name}:${value}`; facets.appendChild(elem); } @@ -67,6 +69,19 @@ $('#query, #facet').on('input', function () { results.appendChild(elem); } + // When we click on a tag we append the facet value + // at the end of the facet query. + $('#facets .tag').on('click', function () { + let name = $(this).attr("data-name"); + let value = $(this).attr("data-value"); + + let facet_query = $('#facet').val().trim(); + if (facet_query === "") { + $('#facet').val(`${name} = "${value}"`).trigger('input'); + } else { + $('#facet').val(`${facet_query} AND ${name} = "${value}"`).trigger('input'); + } + }); }, beforeSend: function () { if (request !== null) { @@ -88,8 +103,8 @@ $('#db-size').text(function(index, text) { return filesize(parseInt(text)) }); -// We trigger the input when we load the script, this way -// we execute a placeholder search when the input is empty. +// We trigger the input when we load the script. $(window).on('load', function () { + // We execute a placeholder search when the input is empty. $('#query').trigger('input'); }); diff --git a/http-ui/public/style.css b/http-ui/public/style.css index 2e58c24ff..1de348082 100644 --- a/http-ui/public/style.css +++ b/http-ui/public/style.css @@ -8,6 +8,7 @@ margin-right: 1em; margin-bottom: 1em; } + #facets { max-width: 900px; margin: 20px auto 0 auto; @@ -16,6 +17,10 @@ overflow: scroll; } +#facets .tag:hover { + cursor: pointer; +} + #logo-white { display: none; } From aa129dd7e8e335f024c75ba0d302bcc57eda57e2 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 29 Dec 2020 00:50:06 +0100 Subject: [PATCH 04/32] Display the number of candidates instead of the returned document count --- http-ui/public/script.js | 2 +- http-ui/src/main.rs | 3 +++ http-ui/templates/index.html | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/http-ui/public/script.js b/http-ui/public/script.js index 58fc74eab..3071f78ac 100644 --- a/http-ui/public/script.js +++ b/http-ui/public/script.js @@ -26,7 +26,7 @@ $('#query, #facet').on('input', function () { let timeSpent = request.getResponseHeader('Time-Ms'); let numberOfDocuments = data.documents.length; - count.innerHTML = `${numberOfDocuments}`; + count.innerHTML = data.numberOfCandidates.toLocaleString(); time.innerHTML = `${timeSpent}ms`; time.classList.remove('fade-in-out'); diff --git a/http-ui/src/main.rs b/http-ui/src/main.rs index fe2f71c8e..46f81ab5e 100644 --- a/http-ui/src/main.rs +++ b/http-ui/src/main.rs @@ -633,6 +633,7 @@ async fn main() -> anyhow::Result<()> { #[serde(rename_all = "camelCase")] struct Answer { documents: Vec>, + number_of_candidates: u64, facets: HashMap>, } @@ -659,6 +660,7 @@ async fn main() -> anyhow::Result<()> { let SearchResult { found_words, candidates, documents_ids } = search.execute().unwrap(); + let number_of_candidates = candidates.len(); let facets = if query.facet_distribution == Some(true) { Some(index.facets(&rtxn).candidates(candidates).execute().unwrap()) } else { @@ -690,6 +692,7 @@ async fn main() -> anyhow::Result<()> { let answer = Answer { documents, + number_of_candidates, facets: facets.unwrap_or_default(), }; diff --git a/http-ui/templates/index.html b/http-ui/templates/index.html index 49d36bb65..20de4884a 100644 --- a/http-ui/templates/index.html +++ b/http-ui/templates/index.html @@ -66,7 +66,7 @@
-
- -
+
+
+
+
+ +
+
-
-
    - -
+
+
    + +
+
+
From cb5e57e2dd7cbca398b671d68e04e1fc6302798f Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Thu, 7 Jan 2021 10:17:27 +0100 Subject: [PATCH 09/32] FacetCondition can be created from array of facets --- src/search/facet/facet_condition.rs | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/search/facet/facet_condition.rs b/src/search/facet/facet_condition.rs index 92331ee7f..ce4a22119 100644 --- a/src/search/facet/facet_condition.rs +++ b/src/search/facet/facet_condition.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; use std::ops::Bound::{self, Included, Excluded}; use std::str::FromStr; +use either::Either; use heed::types::{ByteSlice, DecodeIgnore}; use log::debug; use num_traits::Bounded; @@ -141,6 +142,81 @@ where T: FromStr, } impl FacetCondition { + pub fn from_array( + rtxn: &heed::RoTxn, + index: &Index, + array: I, + ) -> anyhow::Result> + where I: IntoIterator>, + J: IntoIterator, + A: AsRef, + B: AsRef, + { + fn facet_condition( + fields_ids_map: &FieldsIdsMap, + faceted_fields: &HashMap, + key: &str, + value: &str, + ) -> anyhow::Result + { + let fid = fields_ids_map.id(key).unwrap(); + let ftype = faceted_fields.get(&fid).copied().unwrap(); + let (neg, value) = match value.strip_prefix('-') { + Some(value) => (true, value), + None => (false, value), + }; + + let operator = match ftype { + FacetType::String => OperatorString(fid, FacetStringOperator::equal(value)), + FacetType::Float => OperatorF64(fid, FacetNumberOperator::Equal(value.parse()?)), + FacetType::Integer => OperatorI64(fid, FacetNumberOperator::Equal(value.parse()?)), + }; + + if neg { Ok(operator.negate()) } else { Ok(operator) } + } + + let fields_ids_map = index.fields_ids_map(rtxn)?; + let faceted_fields = index.faceted_fields(rtxn)?; + let mut ands = None; + + for either in array { + match either { + Either::Left(array) => { + let mut ors = None; + for rule in array { + let mut iter = rule.as_ref().splitn(2, ':'); + let key = iter.next().unwrap(); + let value = iter.next().unwrap(); + let condition = facet_condition(&fields_ids_map, &faceted_fields, key, value)?; + ors = match ors.take() { + Some(ors) => Some(Or(Box::new(ors), Box::new(condition))), + None => Some(condition), + }; + } + + if let Some(rule) = ors { + ands = match ands.take() { + Some(ands) => Some(And(Box::new(ands), Box::new(rule))), + None => Some(rule), + }; + } + }, + Either::Right(rule) => { + let mut iter = rule.as_ref().splitn(2, ':'); + let key = iter.next().unwrap(); + let value = iter.next().unwrap(); + let condition = facet_condition(&fields_ids_map, &faceted_fields, key, value)?; + ands = match ands.take() { + Some(ands) => Some(And(Box::new(ands), Box::new(condition))), + None => Some(condition), + }; + } + } + } + + Ok(ands) + } + pub fn from_str( rtxn: &heed::RoTxn, index: &Index, From afa86d8a45f20e1e4f1a88d7401ade0f4b6358e8 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Mon, 21 Dec 2020 19:36:14 +0100 Subject: [PATCH 10/32] Add a simple test to the FacetCondition from_array method --- src/search/facet/facet_condition.rs | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/search/facet/facet_condition.rs b/src/search/facet/facet_condition.rs index ce4a22119..762134759 100644 --- a/src/search/facet/facet_condition.rs +++ b/src/search/facet/facet_condition.rs @@ -717,4 +717,35 @@ mod tests { ); assert_eq!(condition, expected); } + + #[test] + fn from_array() { + let path = tempfile::tempdir().unwrap(); + let mut options = EnvOpenOptions::new(); + options.map_size(10 * 1024 * 1024); // 10 MB + let index = Index::new(options, &path).unwrap(); + + // Set the faceted fields to be the channel. + let mut wtxn = index.write_txn().unwrap(); + let mut builder = Settings::new(&mut wtxn, &index); + builder.set_searchable_fields(vec!["channel".into(), "timestamp".into()]); // to keep the fields order + builder.set_faceted_fields(hashmap!{ + "channel".into() => "string".into(), + "timestamp".into() => "integer".into(), + }); + builder.execute(|_| ()).unwrap(); + wtxn.commit().unwrap(); + + // Test that the facet condition is correctly generated. + let rtxn = index.read_txn().unwrap(); + let condition = FacetCondition::from_array( + &rtxn, &index, + vec![Either::Right("channel:gotaga"), Either::Left(vec!["timestamp:44", "channel:-ponce"])], + ).unwrap().unwrap(); + let expected = FacetCondition::from_str( + &rtxn, &index, + "channel = gotaga AND (timestamp = 44 OR channel != ponce)", + ).unwrap(); + assert_eq!(condition, expected); + } } From 33945a311573020081563eb45e410bd986eba5c5 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Thu, 7 Jan 2021 10:15:31 +0100 Subject: [PATCH 11/32] Introduce a new facet filters query field --- http-ui/Cargo.lock | 627 ++++++++++++++++++----------------- http-ui/Cargo.toml | 1 + http-ui/public/script.js | 47 ++- http-ui/src/main.rs | 51 ++- http-ui/templates/index.html | 2 +- 5 files changed, 407 insertions(+), 321 deletions(-) diff --git a/http-ui/Cargo.lock b/http-ui/Cargo.lock index 04f33336f..1ef0ccfbd 100644 --- a/http-ui/Cargo.lock +++ b/http-ui/Cargo.lock @@ -23,21 +23,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.38" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7" [[package]] name = "askama" -version = "0.10.5" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d298738b6e47e1034e560e5afe63aa488fea34e25ec11b855a76f0d7b8e73134" +checksum = "70a6e7ebd44d0047fd48206c83c5cd3214acc7b9d87f001da170145c47ef7d12" dependencies = [ "askama_derive", "askama_escape", @@ -48,12 +42,13 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.10.5" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2925c4c290382f9d2fa3d1c1b6a63fa1427099721ecca4749b154cc9c25522" +checksum = "e1d7169690c4f56343dcd821ab834972a22570a2662a19a84fd7775d5e1c3881" dependencies = [ "askama_shared", "proc-macro2", + "quote", "syn", ] @@ -65,9 +60,9 @@ checksum = "90c108c1a94380c89d2215d0ac54ce09796823cca0fd91b299cfff3b33e346fb" [[package]] name = "askama_shared" -version = "0.11.1" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2582b77e0f3c506ec4838a25fa8a5f97b9bed72bb6d3d272ea1c031d8bd373bc" +checksum = "62fc272363345c8cdc030e4c259d9d028237f8b057dc9bb327772a257bde6bb5" dependencies = [ "askama_escape", "humansize", @@ -102,6 +97,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + [[package]] name = "autocfg" version = "1.0.1" @@ -114,12 +115,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - [[package]] name = "bincode" version = "1.3.1" @@ -136,18 +131,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "bitvec" -version = "0.19.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ba35e9565969edb811639dbebfe34edc0368e472c5018474c8eb2543397f81" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "block-buffer" version = "0.7.3" @@ -217,9 +200,9 @@ dependencies = [ [[package]] name = "byteorder" -version = "1.4.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" @@ -227,17 +210,11 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" -[[package]] -name = "bytes" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" - [[package]] name = "cc" -version = "1.0.66" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" +checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" dependencies = [ "jobserver", ] @@ -298,10 +275,19 @@ dependencies = [ ] [[package]] -name = "const_fn" -version = "0.4.5" +name = "cloudabi" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "const_fn" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab" [[package]] name = "cow-utils" @@ -331,7 +317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.0", ] [[package]] @@ -342,18 +328,18 @@ checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.0", ] [[package]] name = "crossbeam-epoch" -version = "0.9.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" +checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f" dependencies = [ "cfg-if 1.0.0", "const_fn", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.0", "lazy_static", "memoffset", "scopeguard", @@ -380,20 +366,21 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5" dependencies = [ - "autocfg", + "autocfg 1.0.1", "cfg-if 1.0.0", + "const_fn", "lazy_static", ] [[package]] name = "csv" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97" +checksum = "fc4666154fd004af3fd6f1da2e81a96fd5a81927fe8ddb6ecc79e2aa6e138b54" dependencies = [ "bstr", "csv-core", @@ -437,9 +424,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" +checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" [[package]] name = "either" @@ -493,6 +480,12 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d79238883cf0307100b90aba4a755d8051a3182305dfe7f649a1e9dc0517006f" +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -509,17 +502,11 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - [[package]] name = "futures" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" +checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797" dependencies = [ "futures-channel", "futures-core", @@ -532,9 +519,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151" dependencies = [ "futures-core", "futures-sink", @@ -542,15 +529,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" +checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46" [[package]] name = "futures-executor" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" +checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb" dependencies = [ "futures-core", "futures-task", @@ -559,15 +546,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" +checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b" [[package]] name = "futures-macro" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -577,24 +564,24 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" +checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11" [[package]] name = "futures-task" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" +checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c" dependencies = [ "once_cell", ] [[package]] name = "futures-util" -version = "0.3.12" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34" dependencies = [ "futures-channel", "futures-core", @@ -603,7 +590,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.4", + "pin-project 1.0.1", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -640,26 +627,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.16" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] -[[package]] -name = "getrandom" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.10.1+wasi-snapshot-preview1", -] - [[package]] name = "glob" version = "0.3.0" @@ -686,7 +662,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" dependencies = [ - "bytes 0.5.6", + "bytes", "fnv", "futures-core", "futures-sink", @@ -707,7 +683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" dependencies = [ "ahash", - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -718,13 +694,13 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "headers" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62689dc57c7456e69712607ffcbd0aa1dfcccf9af73727e9b25bc1825375cac3" +checksum = "ed18eb2459bf1a09ad2d6b1547840c3e5e62882fa09b9a6a20b1de8e3228848f" dependencies = [ - "base64 0.13.0", + "base64", "bitflags", - "bytes 1.0.1", + "bytes", "headers-core", "http", "mime", @@ -743,18 +719,18 @@ dependencies = [ [[package]] name = "heck" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" dependencies = [ "unicode-segmentation", ] [[package]] name = "heed" -version = "0.10.6" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcc6c911acaadad3ebe9f1ef1707d80bd71c92037566f47b6238a03b60adf1a" +checksum = "2eaba3b0edee6a9cd551f24caca2027922b03259f7203a15f0b86af4c1348fcc" dependencies = [ "byteorder", "heed-traits", @@ -763,7 +739,6 @@ dependencies = [ "lmdb-rkv-sys", "once_cell", "page_size", - "serde", "synchronoise", "url", "zerocopy", @@ -790,20 +765,20 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "libc", ] [[package]] name = "http" -version = "0.2.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" dependencies = [ - "bytes 1.0.1", + "bytes", "fnv", "itoa", ] @@ -814,7 +789,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ - "bytes 0.5.6", + "bytes", "http", ] @@ -826,7 +801,8 @@ dependencies = [ "askama", "askama_warp", "byte-unit", - "bytes 0.5.6", + "bytes", + "either", "flate2", "fst", "futures", @@ -877,7 +853,7 @@ version = "0.13.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" dependencies = [ - "bytes 0.5.6", + "bytes", "futures-channel", "futures-core", "futures-util", @@ -887,7 +863,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project 1.0.4", + "pin-project 1.0.1", "socket2", "tokio", "tower-service", @@ -908,11 +884,11 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ - "autocfg", + "autocfg 1.0.1", "hashbrown 0.9.1", ] @@ -922,7 +898,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" dependencies = [ - "bytes 0.5.6", + "bytes", ] [[package]] @@ -945,9 +921,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "jemalloc-sys" @@ -1019,30 +995,17 @@ dependencies = [ "fst", ] -[[package]] -name = "lexical-core" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if 0.1.10", - "ryu", - "static_assertions", -] - [[package]] name = "libc" -version = "0.2.82" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lmdb-rkv-sys" @@ -1057,9 +1020,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.13" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if 0.1.10", ] @@ -1110,11 +1073,11 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.6.1" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -1186,14 +1149,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" dependencies = [ "adler", - "autocfg", + "autocfg 1.0.1", ] [[package]] name = "mio" -version = "0.6.23" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if 0.1.10", "fuchsia-zircon", @@ -1202,7 +1165,7 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow 0.2.2", + "miow 0.2.1", "net2", "slab", "winapi 0.2.8", @@ -1216,7 +1179,7 @@ checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" dependencies = [ "log", "mio", - "miow 0.3.6", + "miow 0.3.5", "winapi 0.3.9", ] @@ -1233,9 +1196,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" dependencies = [ "kernel32-sys", "net2", @@ -1245,9 +1208,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" dependencies = [ "socket2", "winapi 0.3.9", @@ -1255,9 +1218,9 @@ dependencies = [ [[package]] name = "multipart" -version = "0.17.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050aeedc89243f5347c3e237e3e13dc76fbe4ae3742a57b94dc14f69acf76d4" +checksum = "8209c33c951f07387a8497841122fc6f712165e3f9bda3e6be4645b58188f676" dependencies = [ "buf_redux", "httparse", @@ -1265,7 +1228,7 @@ dependencies = [ "mime", "mime_guess", "quick-error", - "rand 0.7.3", + "rand 0.6.5", "safemem", "tempfile", "twoway", @@ -1276,14 +1239,14 @@ name = "near-proximity" version = "0.1.0" source = "git+https://github.com/Kerollmops/plane-sweep-proximity?rev=6608205#66082058537f6fe7709adc4690048d62f3c0e9b7" dependencies = [ - "tinyvec", + "tinyvec 1.0.1", ] [[package]] name = "net2" -version = "0.2.37" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" dependencies = [ "cfg-if 0.1.10", "libc", @@ -1292,24 +1255,22 @@ dependencies = [ [[package]] name = "nix" -version = "0.19.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" +checksum = "85db2feff6bf70ebc3a4793191517d5f0331100a2f10f9bf93b5e5214f32b7b7" dependencies = [ "bitflags", "cc", - "cfg-if 1.0.0", + "cfg-if 0.1.10", "libc", ] [[package]] name = "nom" -version = "6.0.1" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88034cfd6b4a0d54dd14f4a507eceee36c0b70e5a02236c4e4df571102be17f0" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ - "bitvec", - "lexical-core", "memchr", "version_check", ] @@ -1320,7 +1281,7 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg", + "autocfg 1.0.1", "num-traits", ] @@ -1330,7 +1291,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -1369,9 +1330,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "ordered-float" -version = "2.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dacdec97876ef3ede8c50efc429220641a0b11ba0048b4b0c357bccbc47c5204" +checksum = "9fe9037165d7023b1228bc4ae9a2fa1a2b0095eca6c2998c624723dfd01314a5" dependencies = [ "num-traits", ] @@ -1492,11 +1453,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" +checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" dependencies = [ - "pin-project-internal 1.0.4", + "pin-project-internal 1.0.1", ] [[package]] @@ -1512,9 +1473,9 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "1.0.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" +checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86" dependencies = [ "proc-macro2", "quote", @@ -1527,12 +1488,6 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" -[[package]] -name = "pin-project-lite" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" - [[package]] name = "pin-utils" version = "0.1.0" @@ -1583,9 +1538,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro-nested" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" [[package]] name = "proc-macro2" @@ -1604,18 +1559,31 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.8" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] [[package]] -name = "radium" -version = "0.5.3" +name = "rand" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg 0.1.2", + "rand_xorshift", + "winapi 0.3.9", +] [[package]] name = "rand" @@ -1623,24 +1591,22 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.16", + "getrandom", "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc 0.2.0", - "rand_pcg", + "rand_pcg 0.2.1", ] [[package]] -name = "rand" -version = "0.8.2" +name = "rand_chacha" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "libc", - "rand_chacha 0.3.0", - "rand_core 0.6.1", - "rand_hc 0.3.0", + "autocfg 0.1.7", + "rand_core 0.3.1", ] [[package]] @@ -1654,31 +1620,36 @@ dependencies = [ ] [[package]] -name = "rand_chacha" -version = "0.3.0" +name = "rand_core" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "ppv-lite86", - "rand_core 0.6.1", + "rand_core 0.4.2", ] +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.16", + "getrandom", ] [[package]] -name = "rand_core" -version = "0.6.1" +name = "rand_hc" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" dependencies = [ - "getrandom 0.2.2", + "rand_core 0.3.1", ] [[package]] @@ -1691,12 +1662,47 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.3.0" +name = "rand_isaac" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" dependencies = [ - "rand_core 0.6.1", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi 0.3.9", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi 0.3.9", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.4.2", ] [[package]] @@ -1708,13 +1714,22 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rayon" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" dependencies = [ - "autocfg", + "autocfg 1.0.1", "crossbeam-deque", "either", "rayon-core", @@ -1728,25 +1743,31 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" dependencies = [ "crossbeam-channel", "crossbeam-deque", - "crossbeam-utils 0.8.1", + "crossbeam-utils 0.8.0", "lazy_static", "num_cpus", ] [[package]] -name = "redox_syscall" -version = "0.2.4" +name = "rdrand" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ - "bitflags", + "rand_core 0.3.1", ] [[package]] -name = "regex" -version = "1.4.3" +name = "redox_syscall" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" dependencies = [ "aho-corasick", "memchr", @@ -1765,9 +1786,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.22" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "remove_dir_all" @@ -1786,9 +1807,9 @@ checksum = "21215c1b9d8f7832b433255bd9eea3e2779aa55b21b2f8e13aad62c74749b237" [[package]] name = "roaring" -version = "0.6.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12bdbc3b9b2fd12148ee9f97f9e36438f1e84d3ce47fec0ad6b4bfbb62b3a35" +checksum = "99a260b0fb7df2095948f4a1d37afe5d1a08a2ccc7380f418cec049dc9560077" dependencies = [ "byteorder", ] @@ -1819,18 +1840,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.120" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.120" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", @@ -1839,9 +1860,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.61" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "indexmap", "itoa", @@ -1888,9 +1909,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.3.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" dependencies = [ "libc", ] @@ -1925,38 +1946,33 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.6.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" [[package]] name = "snap" -version = "1.0.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d3306e84bf86710d6cd8b4c9c3b721d5454cc91a603180f8f8cd06cfd317b4" +checksum = "da73c8f77aebc0e40c300b93f0a5f1bece7a248a36eee287d4e095f35c7b7d6e" [[package]] name = "socket2" -version = "0.3.19" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "libc", + "redox_syscall", "winapi 0.3.9", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "stderrlog" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a53e2eff3e94a019afa6265e8ee04cb05b9d33fe9f5078b14e4e391d155a38" +checksum = "b02f316286ae558d83acc93dd81eaba096e746987a7961d4a9ae026842bae67f" dependencies = [ "atty", "chrono", @@ -1967,9 +1983,9 @@ dependencies = [ [[package]] name = "structopt" -version = "0.3.21" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +checksum = "126d630294ec449fae0b16f964e35bf3c74f940da9dca17ee9b905f7b3112eb8" dependencies = [ "clap", "lazy_static", @@ -1978,9 +1994,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.14" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +checksum = "65e51c492f9e23a220534971ff5afc14037289de430e3c83f9daf6a1b6ae91e8" dependencies = [ "heck", "proc-macro-error", @@ -1991,9 +2007,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.58" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" +checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" dependencies = [ "proc-macro2", "quote", @@ -2021,21 +2037,15 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tap" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36474e732d1affd3a6ed582781b3683df3d0563714c59c39591e8ff707cf078e" - [[package]] name = "tempfile" -version = "3.2.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "libc", - "rand 0.8.2", + "rand 0.7.3", "redox_syscall", "remove_dir_all", "winapi 0.3.9", @@ -2053,9 +2063,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" dependencies = [ "winapi-util", ] @@ -2081,19 +2091,26 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi 0.3.9", ] [[package]] name = "tinyvec" -version = "1.1.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" + +[[package]] +name = "tinyvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b78a366903f506d2ad52ca8dc552102ffdd3e937ba8a227f024dc1d1eae28575" dependencies = [ "tinyvec_macros", ] @@ -2106,11 +2123,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.2.24" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" +checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" dependencies = [ - "bytes 0.5.6", + "bytes", "fnv", "futures-core", "iovec", @@ -2121,7 +2138,7 @@ dependencies = [ "mio-named-pipes", "mio-uds", "num_cpus", - "pin-project-lite 0.1.11", + "pin-project-lite", "signal-hook-registry", "slab", "tokio-macros", @@ -2130,9 +2147,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ "proc-macro2", "quote", @@ -2158,19 +2175,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ - "bytes 0.5.6", + "bytes", "futures-core", "futures-sink", "log", - "pin-project-lite 0.1.11", + "pin-project-lite", "tokio", ] [[package]] name = "toml" -version = "0.5.8" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ "serde", ] @@ -2183,13 +2200,13 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tracing" -version = "0.1.22" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "log", - "pin-project-lite 0.2.4", + "pin-project-lite", "tracing-core", ] @@ -2224,9 +2241,9 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" dependencies = [ - "base64 0.12.3", + "base64", "byteorder", - "bytes 0.5.6", + "bytes", "http", "httparse", "input_buffer", @@ -2278,18 +2295,18 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.16" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" dependencies = [ - "tinyvec", + "tinyvec 0.3.4", ] [[package]] name = "unicode-segmentation" -version = "1.7.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-width" @@ -2335,11 +2352,11 @@ checksum = "9071ac216321a4470a69fb2b28cfc68dcd1a39acd877c8be8e014df6772d8efa" [[package]] name = "uuid" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" dependencies = [ - "getrandom 0.2.2", + "rand 0.7.3", ] [[package]] @@ -2364,7 +2381,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f41be6df54c97904af01aa23e613d4521eed7ab23537cede692d4058f6449407" dependencies = [ - "bytes 0.5.6", + "bytes", "futures", "headers", "http", @@ -2394,9 +2411,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.10.1+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "whatlang" @@ -2460,12 +2477,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - [[package]] name = "zerocopy" version = "0.3.0" @@ -2489,18 +2500,18 @@ dependencies = [ [[package]] name = "zstd" -version = "0.5.4+zstd.1.4.7" +version = "0.5.3+zstd.1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69996ebdb1ba8b1517f61387a883857818a66c8a295f487b1ffd8fd9d2c82910" +checksum = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "2.0.6+zstd.1.4.7" +version = "2.0.5+zstd.1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98aa931fb69ecee256d44589d19754e61851ae4769bf963b385119b1cc37a49e" +checksum = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055" dependencies = [ "libc", "zstd-sys", @@ -2508,9 +2519,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "1.4.18+zstd.1.4.7" +version = "1.4.17+zstd.1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6e8778706838f43f771d80d37787cb2fe06dafe89dd3aebaf6721b9eaec81" +checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b" dependencies = [ "cc", "glob", diff --git a/http-ui/Cargo.toml b/http-ui/Cargo.toml index 5fcdf9caf..92932cb00 100644 --- a/http-ui/Cargo.toml +++ b/http-ui/Cargo.toml @@ -22,6 +22,7 @@ tempfile = "3.1.0" askama = "0.10.1" askama_warp = "0.10.0" bytes = "0.5.6" +either = "1.6.1" flate2 = "1.0.19" futures = "0.3.6" serde = { version = "1.0", features = ["derive"] } diff --git a/http-ui/public/script.js b/http-ui/public/script.js index 6a76fe859..f935cc07d 100644 --- a/http-ui/public/script.js +++ b/http-ui/public/script.js @@ -2,9 +2,10 @@ var request = null; var timeoutID = null; var selected_facets = {}; -$('#query, #facet').on('input', function () { +$('#query, #filters').on('input', function () { var query = $('#query').val(); - var facet = $('#facet').val(); + var filters = $('#filters').val(); + var facet_filters = selectedFacetsToArray(selected_facets); var timeoutMs = 100; if (timeoutID !== null) { @@ -17,7 +18,10 @@ $('#query, #facet').on('input', function () { url: "query", contentType: 'application/json', data: JSON.stringify({ - 'query': query, 'facetCondition': facet, "facetDistribution": true + 'query': query, + 'filters': filters, + 'facetFilters': facet_filters, + "facetDistribution": true, }), contentType: 'application/json', success: function (data, textStatus, request) { @@ -41,7 +45,20 @@ $('#query, #facet').on('input', function () { // Create the select element let select = $(``); - for (value of data.facets[facet_name]) { + let selected_values = selected_facets[facet_name] || []; + // Create the previously selected facets (mark them as selected) + for (value of selected_values) { + let option = $('') + .text(value) + .attr('selected', "selected") + .attr('value', value) + .attr('title', value); + select.append(option); + } + + // Create the newly discovered facets + let diff = diffArray(data.facets[facet_name], selected_values); + for (value of diff) { let option = $('') .text(value) .attr('value', value) @@ -53,7 +70,6 @@ $('#query, #facet').on('input', function () { $('#facets').append(div); } - for (element of data.documents) { const elem = document.createElement('li'); elem.classList.add("document"); @@ -87,8 +103,8 @@ $('#query, #facet').on('input', function () { $('#facets select').on('change', function(e) { let facet_name = $(this).attr('data-facet-name'); selected_facets[facet_name] = $(this).val(); + $('#query').trigger('input'); }); - }, beforeSend: function () { if (request !== null) { @@ -100,6 +116,25 @@ $('#query, #facet').on('input', function () { }, timeoutMs); }); +function diffArray(arr1, arr2) { + return arr1.concat(arr2).filter(function (val) { + if (!(arr1.includes(val) && arr2.includes(val))) + return val; + }); +} + +function selectedFacetsToArray(facets_obj) { + var array = []; + for (const facet_name in facets_obj) { + var subarray = []; + for (const facet_value of facets_obj[facet_name]) { + subarray.push(`${facet_name}:${facet_value}`); + } + array.push(subarray); + } + return array; +} + // Make the number of document a little bit prettier $('#docs-count').text(function(index, text) { return parseInt(text).toLocaleString() diff --git a/http-ui/src/main.rs b/http-ui/src/main.rs index 46f81ab5e..6e4f42a4e 100644 --- a/http-ui/src/main.rs +++ b/http-ui/src/main.rs @@ -11,6 +11,7 @@ use std::{mem, io}; use askama_warp::Template; use byte_unit::Byte; +use either::Either; use flate2::read::GzDecoder; use futures::stream; use futures::{FutureExt, StreamExt}; @@ -620,12 +621,29 @@ async fn main() -> anyhow::Result<()> { .body(include_str!("../public/logo-black.svg")) ); + #[derive(Debug, Deserialize)] + #[serde(untagged)] + enum UntaggedEither { + Left(L), + Right(R), + } + + impl From> for Either { + fn from(value: UntaggedEither) -> Either { + match value { + UntaggedEither::Left(left) => Either::Left(left), + UntaggedEither::Right(right) => Either::Right(right), + } + } + } + #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] struct QueryBody { query: Option, - facet_condition: Option, + filters: Option, + facet_filters: Option, String>>>, facet_distribution: Option, } @@ -651,11 +669,32 @@ async fn main() -> anyhow::Result<()> { if let Some(query) = query.query { search.query(query); } - if let Some(condition) = query.facet_condition { - if !condition.trim().is_empty() { - let condition = FacetCondition::from_str(&rtxn, &index, &condition).unwrap(); - search.facet_condition(condition); - } + + let filters = match query.filters { + Some(condition) if !condition.trim().is_empty() => { + Some(FacetCondition::from_str(&rtxn, &index, &condition).unwrap()) + }, + _otherwise => None, + }; + + let facet_filters = match query.facet_filters { + Some(array) => { + let eithers = array.into_iter().map(Into::into); + FacetCondition::from_array(&rtxn, &index, eithers).unwrap() + }, + _otherwise => None, + }; + + let condition = match (filters, facet_filters) { + (Some(filters), Some(facet_filters)) => { + Some(FacetCondition::And(Box::new(filters), Box::new(facet_filters))) + }, + (Some(condition), None) | (None, Some(condition)) => Some(condition), + _otherwise => None, + }; + + if let Some(condition) = condition { + search.facet_condition(condition); } let SearchResult { found_words, candidates, documents_ids } = search.execute().unwrap(); diff --git a/http-ui/templates/index.html b/http-ui/templates/index.html index 114652e7b..adb202b3a 100644 --- a/http-ui/templates/index.html +++ b/http-ui/templates/index.html @@ -56,7 +56,7 @@
- +
From d893e83622018d15633e32190a93ad6fe6152a32 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Wed, 6 Jan 2021 15:10:30 +0100 Subject: [PATCH 12/32] Speed-up facet aggregation by using a FacetIter --- src/search/facet/facet_distribution.rs | 137 ++++++++++++++++--------- src/search/facet/mod.rs | 39 ++++++- src/search/mod.rs | 8 +- 3 files changed, 124 insertions(+), 60 deletions(-) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 2ee297fa2..8256d1234 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -9,7 +9,7 @@ use serde_json::Value; use crate::facet::FacetType; use crate::heed_codec::facet::{FacetValueStringCodec, FacetLevelValueF64Codec, FacetLevelValueI64Codec}; use crate::heed_codec::facet::{FieldDocIdFacetStringCodec, FieldDocIdFacetF64Codec, FieldDocIdFacetI64Codec}; -use crate::search::facet::FacetRange; +use crate::search::facet::{FacetIter, FacetRange}; use crate::{Index, FieldId}; pub struct FacetDistribution<'a> { @@ -41,61 +41,99 @@ impl<'a> FacetDistribution<'a> { } fn facet_values(&self, field_id: FieldId, facet_type: FacetType) -> heed::Result> { - if let Some(candidates) = self.candidates.as_ref().filter(|c| c.len() <= 1000) { - let mut key_buffer = vec![field_id]; - match facet_type { - FacetType::Float => { - let mut facet_values = HashSet::new(); - for docid in candidates { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - for result in iter { - let ((_, _, value), ()) = result?; - facet_values.insert(OrderedFloat(value)); + if let Some(candidates) = self.candidates.as_ref() { + if candidates.len() <= 1000 { + let mut key_buffer = vec![field_id]; + match facet_type { + FacetType::Float => { + let mut facet_values = HashSet::new(); + for docid in candidates { + key_buffer.truncate(1); + key_buffer.extend_from_slice(&docid.to_be_bytes()); + let iter = self.index.field_id_docid_facet_values + .prefix_iter(self.rtxn, &key_buffer)? + .remap_key_type::(); + for result in iter { + let ((_, _, value), ()) = result?; + facet_values.insert(OrderedFloat(value)); + } } - } - Ok(facet_values.into_iter().map(|f| Value::from(*f)).collect()) - }, - FacetType::Integer => { - let mut facet_values = HashSet::new(); - for docid in candidates { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - for result in iter { - let ((_, _, value), ()) = result?; - facet_values.insert(value); + Ok(facet_values.into_iter().map(|f| Value::from(*f)).collect()) + }, + FacetType::Integer => { + let mut facet_values = HashSet::new(); + for docid in candidates { + key_buffer.truncate(1); + key_buffer.extend_from_slice(&docid.to_be_bytes()); + let iter = self.index.field_id_docid_facet_values + .prefix_iter(self.rtxn, &key_buffer)? + .remap_key_type::(); + for result in iter { + let ((_, _, value), ()) = result?; + facet_values.insert(value); + } } - } - Ok(facet_values.into_iter().map(Value::from).collect()) - }, - FacetType::String => { - let mut facet_values = HashSet::new(); - for docid in candidates { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - for result in iter { - let ((_, _, value), ()) = result?; - facet_values.insert(value); + Ok(facet_values.into_iter().map(Value::from).collect()) + }, + FacetType::String => { + let mut facet_values = HashSet::new(); + for docid in candidates { + key_buffer.truncate(1); + key_buffer.extend_from_slice(&docid.to_be_bytes()); + let iter = self.index.field_id_docid_facet_values + .prefix_iter(self.rtxn, &key_buffer)? + .remap_key_type::(); + for result in iter { + let ((_, _, value), ()) = result?; + facet_values.insert(value); + } } + Ok(facet_values.into_iter().map(Value::from).collect()) + }, + } + } else { + let iter = match facet_type { + FacetType::String => { + let db = self.index.facet_field_id_value_docids; + let iter = db + .prefix_iter(self.rtxn, &[field_id])? + .remap_key_type::() + .map(|r| r.map(|((_, v), docids)| (Value::from(v), docids))); + Box::new(iter) as Box::> + }, + FacetType::Integer => { + let iter = FacetIter::::new_non_reducing( + self.rtxn, self.index, field_id, candidates.clone(), + )?; + Box::new(iter.map(|r| r.map(|(v, docids)| (Value::from(v), docids)))) + }, + FacetType::Float => { + let iter = FacetIter::::new_non_reducing( + self.rtxn, self.index, field_id, candidates.clone(), + )?; + Box::new(iter.map(|r| r.map(|(v, docids)| (Value::from(v), docids)))) + }, + }; + + let mut facet_values = Vec::new(); + for result in iter { + let (value, docids) = result?; + if self.candidates.as_ref().map_or(true, |c| docids.is_disjoint(c)) { + facet_values.push(value); } - Ok(facet_values.into_iter().map(Value::from).collect()) - }, + if facet_values.len() == self.max_values_by_facet { + break; + } + } + + Ok(facet_values) } } else { let db = self.index.facet_field_id_value_docids; let iter = match facet_type { FacetType::String => { let iter = db - .prefix_iter(&self.rtxn, &[field_id])? + .prefix_iter(self.rtxn, &[field_id])? .remap_key_type::() .map(|r| r.map(|((_, v), docids)| (Value::from(v), docids))); Box::new(iter) as Box::> @@ -119,11 +157,8 @@ impl<'a> FacetDistribution<'a> { let mut facet_values = Vec::new(); for result in iter { let (value, docids) = result?; - match &self.candidates { - Some(candidates) => if !docids.is_disjoint(candidates) { - facet_values.push(value); - }, - None => facet_values.push(value), + if self.candidates.as_ref().map_or(true, |c| docids.is_disjoint(c)) { + facet_values.push(value); } if facet_values.len() == self.max_values_by_facet { break; diff --git a/src/search/facet/mod.rs b/src/search/facet/mod.rs index 70b5b4658..e5b06185f 100644 --- a/src/search/facet/mod.rs +++ b/src/search/facet/mod.rs @@ -147,6 +147,7 @@ pub struct FacetIter<'t, T: 't, KC> { db: Database, field_id: FieldId, level_iters: Vec<(RoaringBitmap, Either, FacetRevRange<'t, T, KC>>)>, + must_reduce: bool, } impl<'t, T, KC> FacetIter<'t, T, KC> @@ -155,7 +156,10 @@ where KC: for<'a> BytesEncode<'a, EItem = (FieldId, u8, T, T)>, T: PartialOrd + Copy + Bounded, { - pub fn new( + /// Create a `FacetIter` that will iterate on the different facet entries + /// (facet value + documents ids) and that will reduce the given documents ids + /// while iterating on the different facet levels. + pub fn new_reducing( rtxn: &'t heed::RoTxn, index: &'t Index, field_id: FieldId, @@ -165,10 +169,14 @@ where let db = index.facet_field_id_value_docids.remap_key_type::(); let highest_level = Self::highest_level(rtxn, db, field_id)?.unwrap_or(0); let highest_iter = FacetRange::new(rtxn, db, field_id, highest_level, Unbounded, Unbounded)?; - Ok(FacetIter { rtxn, db, field_id, level_iters: vec![(documents_ids, Left(highest_iter))] }) + let level_iters = vec![(documents_ids, Left(highest_iter))]; + Ok(FacetIter { rtxn, db, field_id, level_iters, must_reduce: true }) } - pub fn new_reverse( + /// Create a `FacetIter` that will iterate on the different facet entries in reverse + /// (facet value + documents ids) and that will reduce the given documents ids + /// while iterating on the different facet levels. + pub fn new_reverse_reducing( rtxn: &'t heed::RoTxn, index: &'t Index, field_id: FieldId, @@ -178,7 +186,26 @@ where let db = index.facet_field_id_value_docids.remap_key_type::(); let highest_level = Self::highest_level(rtxn, db, field_id)?.unwrap_or(0); let highest_iter = FacetRevRange::new(rtxn, db, field_id, highest_level, Unbounded, Unbounded)?; - Ok(FacetIter { rtxn, db, field_id, level_iters: vec![(documents_ids, Right(highest_iter))] }) + let level_iters = vec![(documents_ids, Right(highest_iter))]; + Ok(FacetIter { rtxn, db, field_id, level_iters, must_reduce: true }) + } + + /// Create a `FacetIter` that will iterate on the different facet entries + /// (facet value + documents ids) and that will not reduce the given documents ids + /// while iterating on the different facet levels, possibly returning multiple times + /// a document id associated with multiple facet values. + pub fn new_non_reducing( + rtxn: &'t heed::RoTxn, + index: &'t Index, + field_id: FieldId, + documents_ids: RoaringBitmap, + ) -> heed::Result> + { + let db = index.facet_field_id_value_docids.remap_key_type::(); + let highest_level = Self::highest_level(rtxn, db, field_id)?.unwrap_or(0); + let highest_iter = FacetRange::new(rtxn, db, field_id, highest_level, Unbounded, Unbounded)?; + let level_iters = vec![(documents_ids, Left(highest_iter))]; + Ok(FacetIter { rtxn, db, field_id, level_iters, must_reduce: false }) } fn highest_level(rtxn: &'t heed::RoTxn, db: Database, fid: FieldId) -> heed::Result> { @@ -216,7 +243,9 @@ where docids.intersect_with(&documents_ids); if !docids.is_empty() { - documents_ids.difference_with(&docids); + if self.must_reduce { + documents_ids.difference_with(&docids); + } if level == 0 { debug!("found {:?} at {:?}", docids, left); diff --git a/src/search/mod.rs b/src/search/mod.rs index 05999caed..459b301a6 100644 --- a/src/search/mod.rs +++ b/src/search/mod.rs @@ -189,9 +189,9 @@ impl<'a> Search<'a> { } } else { let facet_fn = if ascending { - FacetIter::::new + FacetIter::::new_reducing } else { - FacetIter::::new_reverse + FacetIter::::new_reverse_reducing }; let mut limit_tmp = limit; let mut output = Vec::new(); @@ -226,9 +226,9 @@ impl<'a> Search<'a> { } } else { let facet_fn = if ascending { - FacetIter::::new + FacetIter::::new_reducing } else { - FacetIter::::new_reverse + FacetIter::::new_reverse_reducing }; let mut limit_tmp = limit; let mut output = Vec::new(); From 51a37de885c0d0e7fd82273153eae58c96be966d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 13 Jan 2021 11:30:27 +0100 Subject: [PATCH 13/32] Introduce the FacetValue enum type --- Cargo.lock | 1 + Cargo.toml | 2 +- src/facet/mod.rs | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index d98b7fd8e..1296a517f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -869,6 +869,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dacdec97876ef3ede8c50efc429220641a0b11ba0048b4b0c357bccbc47c5204" dependencies = [ "num-traits", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 52b25cfde..2e4ea13a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ near-proximity = { git = "https://github.com/Kerollmops/plane-sweep-proximity", num-traits = "0.2.14" obkv = "0.1.0" once_cell = "1.4.0" -ordered-float = "2.0.0" +ordered-float = { version = "2.0.0", features = ["serde"] } rayon = "1.3.1" regex = "1.4.2" ringtail = "0.3.0" diff --git a/src/facet/mod.rs b/src/facet/mod.rs index 9ec99f2d3..274d2588d 100644 --- a/src/facet/mod.rs +++ b/src/facet/mod.rs @@ -1,4 +1,6 @@ mod facet_type; +mod facet_value; pub mod value_encoding; pub use self::facet_type::FacetType; +pub use self::facet_value::FacetValue; From 4b9e81fc89c826827bf2a2042bd6201fdfeb04d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 13 Jan 2021 11:59:16 +0100 Subject: [PATCH 14/32] Order the facet values lexicographically --- src/facet/facet_value.rs | 40 ++++++++++++++++++++ src/search/facet/facet_distribution.rs | 52 +++++++++++++------------- 2 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 src/facet/facet_value.rs diff --git a/src/facet/facet_value.rs b/src/facet/facet_value.rs new file mode 100644 index 000000000..823eddcee --- /dev/null +++ b/src/facet/facet_value.rs @@ -0,0 +1,40 @@ +use ordered_float::OrderedFloat; +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize)] +pub enum FacetValue { + String(String), + Float(OrderedFloat), + Integer(i64), +} + +impl From for FacetValue { + fn from(string: String) -> FacetValue { + FacetValue::String(string) + } +} + +impl From<&str> for FacetValue { + fn from(string: &str) -> FacetValue { + FacetValue::String(string.to_owned()) + } +} + +impl From for FacetValue { + fn from(float: f64) -> FacetValue { + FacetValue::Float(OrderedFloat(float)) + } +} + +impl From> for FacetValue { + fn from(float: OrderedFloat) -> FacetValue { + FacetValue::Float(float) + } +} + +impl From for FacetValue { + fn from(integer: i64) -> FacetValue { + FacetValue::Integer(integer) + } +} diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 8256d1234..244169fc2 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -1,12 +1,10 @@ -use std::collections::{HashSet, HashMap}; -use std::{cmp, fmt}; +use std::collections::{HashSet, BTreeSet, BTreeMap}; use std::ops::Bound::Unbounded; +use std::{cmp, fmt}; -use ordered_float::OrderedFloat; use roaring::RoaringBitmap; -use serde_json::Value; -use crate::facet::FacetType; +use crate::facet::{FacetType, FacetValue}; use crate::heed_codec::facet::{FacetValueStringCodec, FacetLevelValueF64Codec, FacetLevelValueI64Codec}; use crate::heed_codec::facet::{FieldDocIdFacetStringCodec, FieldDocIdFacetF64Codec, FieldDocIdFacetI64Codec}; use crate::search::facet::{FacetIter, FacetRange}; @@ -40,13 +38,13 @@ impl<'a> FacetDistribution<'a> { self } - fn facet_values(&self, field_id: FieldId, facet_type: FacetType) -> heed::Result> { + fn facet_values(&self, field_id: FieldId, facet_type: FacetType) -> heed::Result> { if let Some(candidates) = self.candidates.as_ref() { if candidates.len() <= 1000 { let mut key_buffer = vec![field_id]; match facet_type { FacetType::Float => { - let mut facet_values = HashSet::new(); + let mut facet_values = BTreeSet::new(); for docid in candidates { key_buffer.truncate(1); key_buffer.extend_from_slice(&docid.to_be_bytes()); @@ -55,13 +53,13 @@ impl<'a> FacetDistribution<'a> { .remap_key_type::(); for result in iter { let ((_, _, value), ()) = result?; - facet_values.insert(OrderedFloat(value)); + facet_values.insert(FacetValue::from(value)); } } - Ok(facet_values.into_iter().map(|f| Value::from(*f)).collect()) + Ok(facet_values) }, FacetType::Integer => { - let mut facet_values = HashSet::new(); + let mut facet_values = BTreeSet::new(); for docid in candidates { key_buffer.truncate(1); key_buffer.extend_from_slice(&docid.to_be_bytes()); @@ -70,13 +68,13 @@ impl<'a> FacetDistribution<'a> { .remap_key_type::(); for result in iter { let ((_, _, value), ()) = result?; - facet_values.insert(value); + facet_values.insert(FacetValue::from(value)); } } - Ok(facet_values.into_iter().map(Value::from).collect()) + Ok(facet_values) }, FacetType::String => { - let mut facet_values = HashSet::new(); + let mut facet_values = BTreeSet::new(); for docid in candidates { key_buffer.truncate(1); key_buffer.extend_from_slice(&docid.to_be_bytes()); @@ -85,10 +83,10 @@ impl<'a> FacetDistribution<'a> { .remap_key_type::(); for result in iter { let ((_, _, value), ()) = result?; - facet_values.insert(value); + facet_values.insert(FacetValue::from(value)); } } - Ok(facet_values.into_iter().map(Value::from).collect()) + Ok(facet_values) }, } } else { @@ -98,28 +96,28 @@ impl<'a> FacetDistribution<'a> { let iter = db .prefix_iter(self.rtxn, &[field_id])? .remap_key_type::() - .map(|r| r.map(|((_, v), docids)| (Value::from(v), docids))); + .map(|r| r.map(|((_, v), docids)| (FacetValue::from(v), docids))); Box::new(iter) as Box::> }, FacetType::Integer => { let iter = FacetIter::::new_non_reducing( self.rtxn, self.index, field_id, candidates.clone(), )?; - Box::new(iter.map(|r| r.map(|(v, docids)| (Value::from(v), docids)))) + Box::new(iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids)))) }, FacetType::Float => { let iter = FacetIter::::new_non_reducing( self.rtxn, self.index, field_id, candidates.clone(), )?; - Box::new(iter.map(|r| r.map(|(v, docids)| (Value::from(v), docids)))) + Box::new(iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids)))) }, }; - let mut facet_values = Vec::new(); + let mut facet_values = BTreeSet::new(); for result in iter { let (value, docids) = result?; if self.candidates.as_ref().map_or(true, |c| docids.is_disjoint(c)) { - facet_values.push(value); + facet_values.insert(value); } if facet_values.len() == self.max_values_by_facet { break; @@ -135,7 +133,7 @@ impl<'a> FacetDistribution<'a> { let iter = db .prefix_iter(self.rtxn, &[field_id])? .remap_key_type::() - .map(|r| r.map(|((_, v), docids)| (Value::from(v), docids))); + .map(|r| r.map(|((_, v), docids)| (FacetValue::from(v), docids))); Box::new(iter) as Box::> }, FacetType::Integer => { @@ -143,22 +141,22 @@ impl<'a> FacetDistribution<'a> { let range = FacetRange::::new( self.rtxn, db, field_id, 0, Unbounded, Unbounded, )?; - Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (Value::from(v), docids)))) + Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (FacetValue::from(v), docids)))) }, FacetType::Float => { let db = db.remap_key_type::(); let range = FacetRange::::new( self.rtxn, db, field_id, 0, Unbounded, Unbounded, )?; - Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (Value::from(v), docids)))) + Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (FacetValue::from(v), docids)))) }, }; - let mut facet_values = Vec::new(); + let mut facet_values = BTreeSet::new(); for result in iter { let (value, docids) = result?; if self.candidates.as_ref().map_or(true, |c| docids.is_disjoint(c)) { - facet_values.push(value); + facet_values.insert(value); } if facet_values.len() == self.max_values_by_facet { break; @@ -169,7 +167,7 @@ impl<'a> FacetDistribution<'a> { } } - pub fn execute(&self) -> heed::Result>> { + pub fn execute(&self) -> heed::Result>> { let fields_ids_map = self.index.fields_ids_map(self.rtxn)?; let faceted_fields = self.index.faceted_fields(self.rtxn)?; let fields_ids: Vec<_> = match &self.facets { @@ -182,7 +180,7 @@ impl<'a> FacetDistribution<'a> { None => faceted_fields.iter().map(|(id, t)| (*id, *t)).collect(), }; - let mut facets_values = HashMap::new(); + let mut facets_values = BTreeMap::new(); for (fid, ftype) in fields_ids { let facet_name = fields_ids_map.name(fid).unwrap(); let values = self.facet_values(fid, ftype)?; From 7be275b6925d36e3a49dfaf8dcb98ab7a789af15 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 26 Jan 2021 14:14:37 +0100 Subject: [PATCH 15/32] Add the count to the facet distribution --- http-ui/Cargo.lock | 1 + http-ui/public/script.js | 2 +- http-ui/src/main.rs | 5 ++-- src/facet/facet_value.rs | 22 +++++++++++++-- src/search/facet/facet_distribution.rs | 38 ++++++++++++++------------ 5 files changed, 46 insertions(+), 22 deletions(-) diff --git a/http-ui/Cargo.lock b/http-ui/Cargo.lock index 1ef0ccfbd..cb5103003 100644 --- a/http-ui/Cargo.lock +++ b/http-ui/Cargo.lock @@ -1335,6 +1335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fe9037165d7023b1228bc4ae9a2fa1a2b0095eca6c2998c624723dfd01314a5" dependencies = [ "num-traits", + "serde", ] [[package]] diff --git a/http-ui/public/script.js b/http-ui/public/script.js index f935cc07d..9887f06ce 100644 --- a/http-ui/public/script.js +++ b/http-ui/public/script.js @@ -57,7 +57,7 @@ $('#query, #filters').on('input', function () { } // Create the newly discovered facets - let diff = diffArray(data.facets[facet_name], selected_values); + let diff = diffArray(Object.keys(data.facets[facet_name]), selected_values); for (value of diff) { let option = $('') .text(value) diff --git a/http-ui/src/main.rs b/http-ui/src/main.rs index 6e4f42a4e..54f87c3e6 100644 --- a/http-ui/src/main.rs +++ b/http-ui/src/main.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::fmt::Display; use std::fs::{File, create_dir_all}; use std::net::SocketAddr; @@ -29,6 +29,7 @@ use warp::filters::ws::Message; use warp::{Filter, http::Response}; use meilisearch_tokenizer::{Analyzer, AnalyzerConfig}; +use milli::facet::FacetValue; use milli::update::UpdateIndexingStep::*; use milli::update::{UpdateBuilder, IndexDocumentsMethod, UpdateFormat}; use milli::{obkv_to_json, Index, UpdateStore, SearchResult, FacetCondition}; @@ -652,7 +653,7 @@ async fn main() -> anyhow::Result<()> { struct Answer { documents: Vec>, number_of_candidates: u64, - facets: HashMap>, + facets: BTreeMap>, } let disable_highlighting = opt.disable_highlighting; diff --git a/src/facet/facet_value.rs b/src/facet/facet_value.rs index 823eddcee..9630c6634 100644 --- a/src/facet/facet_value.rs +++ b/src/facet/facet_value.rs @@ -1,8 +1,7 @@ use ordered_float::OrderedFloat; -use serde::{Serialize, Deserialize}; +use serde::{Serialize, Serializer}; #[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] -#[derive(Serialize, Deserialize)] pub enum FacetValue { String(String), Float(OrderedFloat), @@ -38,3 +37,22 @@ impl From for FacetValue { FacetValue::Integer(integer) } } + +impl Serialize for FacetValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + FacetValue::String(string) => serializer.serialize_str(string), + FacetValue::Float(float) => { + let string = float.to_string(); + serializer.serialize_str(&string) + }, + FacetValue::Integer(integer) => { + let string = integer.to_string(); + serializer.serialize_str(&string) + }, + } + } +} diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 244169fc2..55b529308 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -1,4 +1,4 @@ -use std::collections::{HashSet, BTreeSet, BTreeMap}; +use std::collections::{HashSet, BTreeMap}; use std::ops::Bound::Unbounded; use std::{cmp, fmt}; @@ -38,13 +38,18 @@ impl<'a> FacetDistribution<'a> { self } - fn facet_values(&self, field_id: FieldId, facet_type: FacetType) -> heed::Result> { + fn facet_values( + &self, + field_id: FieldId, + facet_type: FacetType, + ) -> heed::Result> + { if let Some(candidates) = self.candidates.as_ref() { if candidates.len() <= 1000 { let mut key_buffer = vec![field_id]; match facet_type { FacetType::Float => { - let mut facet_values = BTreeSet::new(); + let mut facet_values = BTreeMap::new(); for docid in candidates { key_buffer.truncate(1); key_buffer.extend_from_slice(&docid.to_be_bytes()); @@ -53,13 +58,13 @@ impl<'a> FacetDistribution<'a> { .remap_key_type::(); for result in iter { let ((_, _, value), ()) = result?; - facet_values.insert(FacetValue::from(value)); + *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; } } Ok(facet_values) }, FacetType::Integer => { - let mut facet_values = BTreeSet::new(); + let mut facet_values = BTreeMap::new(); for docid in candidates { key_buffer.truncate(1); key_buffer.extend_from_slice(&docid.to_be_bytes()); @@ -68,13 +73,13 @@ impl<'a> FacetDistribution<'a> { .remap_key_type::(); for result in iter { let ((_, _, value), ()) = result?; - facet_values.insert(FacetValue::from(value)); + *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; } } Ok(facet_values) }, FacetType::String => { - let mut facet_values = BTreeSet::new(); + let mut facet_values = BTreeMap::new(); for docid in candidates { key_buffer.truncate(1); key_buffer.extend_from_slice(&docid.to_be_bytes()); @@ -83,7 +88,7 @@ impl<'a> FacetDistribution<'a> { .remap_key_type::(); for result in iter { let ((_, _, value), ()) = result?; - facet_values.insert(FacetValue::from(value)); + *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; } } Ok(facet_values) @@ -113,11 +118,12 @@ impl<'a> FacetDistribution<'a> { }, }; - let mut facet_values = BTreeSet::new(); + let mut facet_values = BTreeMap::new(); for result in iter { - let (value, docids) = result?; - if self.candidates.as_ref().map_or(true, |c| docids.is_disjoint(c)) { - facet_values.insert(value); + let (value, mut docids) = result?; + docids.intersect_with(candidates); + if !docids.is_empty() { + facet_values.insert(value, docids.len()); } if facet_values.len() == self.max_values_by_facet { break; @@ -152,12 +158,10 @@ impl<'a> FacetDistribution<'a> { }, }; - let mut facet_values = BTreeSet::new(); + let mut facet_values = BTreeMap::new(); for result in iter { let (value, docids) = result?; - if self.candidates.as_ref().map_or(true, |c| docids.is_disjoint(c)) { - facet_values.insert(value); - } + facet_values.insert(value, docids.len()); if facet_values.len() == self.max_values_by_facet { break; } @@ -167,7 +171,7 @@ impl<'a> FacetDistribution<'a> { } } - pub fn execute(&self) -> heed::Result>> { + pub fn execute(&self) -> heed::Result>> { let fields_ids_map = self.index.fields_ids_map(self.rtxn)?; let faceted_fields = self.index.faceted_fields(self.rtxn)?; let fields_ids: Vec<_> = match &self.facets { From b0c31500fc0e6bce9ad8ffa86aea4f6b31902e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 20 Jan 2021 13:28:34 +0100 Subject: [PATCH 16/32] Simplify the front page --- http-ui/public/script.js | 65 ++++++++++++------------------------ http-ui/public/style.css | 27 +++++++++++++++ http-ui/templates/index.html | 20 ++++------- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/http-ui/public/script.js b/http-ui/public/script.js index 9887f06ce..4a16e8fc3 100644 --- a/http-ui/public/script.js +++ b/http-ui/public/script.js @@ -1,11 +1,9 @@ var request = null; var timeoutID = null; -var selected_facets = {}; $('#query, #filters').on('input', function () { var query = $('#query').val(); var filters = $('#filters').val(); - var facet_filters = selectedFacetsToArray(selected_facets); var timeoutMs = 100; if (timeoutID !== null) { @@ -20,12 +18,11 @@ $('#query, #filters').on('input', function () { data: JSON.stringify({ 'query': query, 'filters': filters, - 'facetFilters': facet_filters, "facetDistribution": true, }), contentType: 'application/json', success: function (data, textStatus, request) { - documents.innerHTML = ''; + results.innerHTML = ''; facets.innerHTML = ''; let timeSpent = request.getResponseHeader('Time-Ms'); @@ -35,39 +32,15 @@ $('#query, #filters').on('input', function () { time.classList.remove('fade-in-out'); for (facet_name in data.facets) { - // Append an header to the list of facets - let upperCaseName = facet_name.charAt(0).toUpperCase() + facet_name.slice(1); - $("

").text(upperCaseName).appendTo($('#facets')); - - // Create a div for a bulma select - const header = document.createElement('div'); - let div = $("
").addClass('select is-multiple'); - - // Create the select element - let select = $(``); - let selected_values = selected_facets[facet_name] || []; - // Create the previously selected facets (mark them as selected) - for (value of selected_values) { - let option = $('') - .text(value) - .attr('selected', "selected") - .attr('value', value) - .attr('title', value); - select.append(option); + for (value in data.facets[facet_name]) { + const elem = document.createElement('span'); + const count = data.facets[facet_name][value]; + elem.classList.add("tag"); + elem.setAttribute('data-name', facet_name); + elem.setAttribute('data-value', value); + elem.innerHTML = `${facet_name}:${value} (${count})`; + facets.appendChild(elem); } - - // Create the newly discovered facets - let diff = diffArray(Object.keys(data.facets[facet_name]), selected_values); - for (value of diff) { - let option = $('') - .text(value) - .attr('value', value) - .attr('title', value); - select.append(option); - } - - div.append(select); - $('#facets').append(div); } for (element of data.documents) { @@ -95,15 +68,21 @@ $('#query, #filters').on('input', function () { } elem.appendChild(ol); - documents.appendChild(elem); + results.appendChild(elem); } - // When we click on a facet value we change the global values - // to make sure that we don't loose the selection between requests. - $('#facets select').on('change', function(e) { - let facet_name = $(this).attr('data-facet-name'); - selected_facets[facet_name] = $(this).val(); - $('#query').trigger('input'); + // When we click on a tag we append the facet value + // at the end of the facet query. + $('#facets .tag').on('click', function () { + let name = $(this).attr("data-name"); + let value = $(this).attr("data-value"); + + let facet_query = $('#filters').val().trim(); + if (facet_query === "") { + $('#filters').val(`${name} = "${value}"`).trigger('input'); + } else { + $('#filters').val(`${facet_query} AND ${name} = "${value}"`).trigger('input'); + } }); }, beforeSend: function () { diff --git a/http-ui/public/style.css b/http-ui/public/style.css index c7ce75537..1de348082 100644 --- a/http-ui/public/style.css +++ b/http-ui/public/style.css @@ -1,5 +1,24 @@ #results { + max-width: 900px; margin: 20px auto 0 auto; + padding: 0; +} + +#facets .tag { + margin-right: 1em; + margin-bottom: 1em; +} + +#facets { + max-width: 900px; + margin: 20px auto 0 auto; + padding: 0; + max-height: 16em; + overflow: scroll; +} + +#facets .tag:hover { + cursor: pointer; } #logo-white { @@ -61,6 +80,14 @@ opacity: 0.7; } +.content { + max-width: 65%; + flex: 0 0 65%; + box-sizing: border-box; + padding-left: 10px; + color: rgba(0,0,0,.9); +} + .content mark { background-color: hsl(204, 86%, 88%); color: hsl(204, 86%, 25%); diff --git a/http-ui/templates/index.html b/http-ui/templates/index.html index adb202b3a..83b1a3e49 100644 --- a/http-ui/templates/index.html +++ b/http-ui/templates/index.html @@ -84,20 +84,14 @@ -
-
-
-
- -
-
+
+ +
-
-
    - -
-
-
+
+
    + +
From 916dd3b7c5435c3b6408136e8f1399a2d63ba7ee Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 26 Jan 2021 14:12:16 +0100 Subject: [PATCH 17/32] Use the faceted_fields_ids method to fetch the ids --- src/search/facet/facet_condition.rs | 2 +- src/search/facet/facet_distribution.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search/facet/facet_condition.rs b/src/search/facet/facet_condition.rs index 762134759..19542a3ab 100644 --- a/src/search/facet/facet_condition.rs +++ b/src/search/facet/facet_condition.rs @@ -176,7 +176,7 @@ impl FacetCondition { } let fields_ids_map = index.fields_ids_map(rtxn)?; - let faceted_fields = index.faceted_fields(rtxn)?; + let faceted_fields = index.faceted_fields_ids(rtxn)?; let mut ands = None; for either in array { diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 55b529308..06c84bf17 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -173,7 +173,7 @@ impl<'a> FacetDistribution<'a> { pub fn execute(&self) -> heed::Result>> { let fields_ids_map = self.index.fields_ids_map(self.rtxn)?; - let faceted_fields = self.index.faceted_fields(self.rtxn)?; + let faceted_fields = self.index.faceted_fields_ids(self.rtxn)?; let fields_ids: Vec<_> = match &self.facets { Some(names) => { names.iter().filter_map(|n| { From 61dbcfa44ad777d44fdca205815b8c6ee067ccad Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 26 Jan 2021 14:38:43 +0100 Subject: [PATCH 18/32] Bump the roaring to 0.6.4 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- http-ui/Cargo.lock | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1296a517f..5f0d68ac4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1212,9 +1212,9 @@ checksum = "21215c1b9d8f7832b433255bd9eea3e2779aa55b21b2f8e13aad62c74749b237" [[package]] name = "roaring" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12bdbc3b9b2fd12148ee9f97f9e36438f1e84d3ce47fec0ad6b4bfbb62b3a35" +checksum = "4d60b41c8f25d07cecab125cb46ebbf234fc055effc61ca2392a3ef4f9422304" dependencies = [ "byteorder", ] diff --git a/Cargo.toml b/Cargo.toml index 2e4ea13a8..0acfb494b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ ordered-float = { version = "2.0.0", features = ["serde"] } rayon = "1.3.1" regex = "1.4.2" ringtail = "0.3.0" -roaring = "0.6.1" +roaring = "0.6.4" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0.59", features = ["preserve_order"] } slice-group-by = "0.2.6" diff --git a/http-ui/Cargo.lock b/http-ui/Cargo.lock index cb5103003..06ed2cf1a 100644 --- a/http-ui/Cargo.lock +++ b/http-ui/Cargo.lock @@ -1808,9 +1808,9 @@ checksum = "21215c1b9d8f7832b433255bd9eea3e2779aa55b21b2f8e13aad62c74749b237" [[package]] name = "roaring" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a260b0fb7df2095948f4a1d37afe5d1a08a2ccc7380f418cec049dc9560077" +checksum = "4d60b41c8f25d07cecab125cb46ebbf234fc055effc61ca2392a3ef4f9422304" dependencies = [ "byteorder", ] From 70e9b1e9363f618fb898efa3df1f8c304b14073d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 20 Jan 2021 17:11:36 +0100 Subject: [PATCH 19/32] Introduce a flag to the search subcommand to display the facet distribution --- src/subcommand/search.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/subcommand/search.rs b/src/subcommand/search.rs index d816c47fe..178ef9941 100644 --- a/src/subcommand/search.rs +++ b/src/subcommand/search.rs @@ -29,6 +29,10 @@ pub struct Opt { /// The query string to search for (doesn't support prefix search yet). query: Option, + + /// Compute and print the facet distribution of all the faceted fields. + #[structopt(long)] + print_facet_distribution: bool, } pub fn run(opt: Opt) -> anyhow::Result<()> { @@ -71,6 +75,12 @@ pub fn run(opt: Opt) -> anyhow::Result<()> { let _ = writeln!(&mut stdout); } + if opt.print_facet_distribution { + let facets = index.facets(&rtxn).candidates(result.candidates).execute()?; + serde_json::to_writer(&mut stdout, &facets)?; + let _ = writeln!(&mut stdout); + } + debug!("Took {:.02?} to find {} documents", before.elapsed(), result.documents_ids.len()); } From 433ac8c38a814f96e4aa3a7799192c128afbb076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 14:11:10 +0100 Subject: [PATCH 20/32] Remove the ordered-float serde feature --- Cargo.lock | 1 - Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f0d68ac4..ba477336e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -869,7 +869,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dacdec97876ef3ede8c50efc429220641a0b11ba0048b4b0c357bccbc47c5204" dependencies = [ "num-traits", - "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0acfb494b..d9ad19e78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ near-proximity = { git = "https://github.com/Kerollmops/plane-sweep-proximity", num-traits = "0.2.14" obkv = "0.1.0" once_cell = "1.4.0" -ordered-float = { version = "2.0.0", features = ["serde"] } +ordered-float = "2.0.0" rayon = "1.3.1" regex = "1.4.2" ringtail = "0.3.0" From 65b821b192105da64daacae5d8dcf7812598b2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 14:15:33 +0100 Subject: [PATCH 21/32] Rename the Index facets method into facets_distribution --- http-ui/Cargo.lock | 1 - http-ui/src/main.rs | 2 +- src/index.rs | 2 +- src/subcommand/search.rs | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/http-ui/Cargo.lock b/http-ui/Cargo.lock index 06ed2cf1a..29e04d714 100644 --- a/http-ui/Cargo.lock +++ b/http-ui/Cargo.lock @@ -1335,7 +1335,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fe9037165d7023b1228bc4ae9a2fa1a2b0095eca6c2998c624723dfd01314a5" dependencies = [ "num-traits", - "serde", ] [[package]] diff --git a/http-ui/src/main.rs b/http-ui/src/main.rs index 54f87c3e6..5c61d3e75 100644 --- a/http-ui/src/main.rs +++ b/http-ui/src/main.rs @@ -702,7 +702,7 @@ async fn main() -> anyhow::Result<()> { let number_of_candidates = candidates.len(); let facets = if query.facet_distribution == Some(true) { - Some(index.facets(&rtxn).candidates(candidates).execute().unwrap()) + Some(index.facets_distribution(&rtxn).candidates(candidates).execute().unwrap()) } else { None }; diff --git a/src/index.rs b/src/index.rs index 6020e332c..c0dd22986 100644 --- a/src/index.rs +++ b/src/index.rs @@ -351,7 +351,7 @@ impl Index { Ok(self.documents_ids(rtxn).map(|docids| docids.len() as usize)?) } - pub fn facets<'a>(&'a self, rtxn: &'a RoTxn) -> FacetDistribution<'a> { + pub fn facets_distribution<'a>(&'a self, rtxn: &'a RoTxn) -> FacetDistribution<'a> { FacetDistribution::new(rtxn, self) } diff --git a/src/subcommand/search.rs b/src/subcommand/search.rs index 178ef9941..0a150209e 100644 --- a/src/subcommand/search.rs +++ b/src/subcommand/search.rs @@ -76,7 +76,7 @@ pub fn run(opt: Opt) -> anyhow::Result<()> { } if opt.print_facet_distribution { - let facets = index.facets(&rtxn).candidates(result.candidates).execute()?; + let facets = index.facets_distribution(&rtxn).candidates(result.candidates).execute()?; serde_json::to_writer(&mut stdout, &facets)?; let _ = writeln!(&mut stdout); } From 60480a1e2ff3c641a11ba50aeaa326c45c15f367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 14:25:53 +0100 Subject: [PATCH 22/32] Rework the FacetCondition from_array constructor --- src/search/facet/facet_condition.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/search/facet/facet_condition.rs b/src/search/facet/facet_condition.rs index 19542a3ab..8e7d16a83 100644 --- a/src/search/facet/facet_condition.rs +++ b/src/search/facet/facet_condition.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; use std::ops::Bound::{self, Included, Excluded}; use std::str::FromStr; +use anyhow::Context; use either::Either; use heed::types::{ByteSlice, DecodeIgnore}; use log::debug; @@ -154,16 +155,20 @@ impl FacetCondition { { fn facet_condition( fields_ids_map: &FieldsIdsMap, - faceted_fields: &HashMap, + faceted_fields: &HashMap, key: &str, value: &str, ) -> anyhow::Result { - let fid = fields_ids_map.id(key).unwrap(); - let ftype = faceted_fields.get(&fid).copied().unwrap(); - let (neg, value) = match value.strip_prefix('-') { - Some(value) => (true, value), - None => (false, value), + let fid = fields_ids_map.id(key).with_context(|| { + format!("{:?} must isn't part of the fields ids map", key) + })?; + let ftype = faceted_fields.get(key).copied().with_context(|| { + format!("{:?} must isn't a faceted field", key) + })?; + let (neg, value) = match value.trim().strip_prefix('-') { + Some(value) => (true, value.trim()), + None => (false, value.trim()), }; let operator = match ftype { @@ -176,7 +181,7 @@ impl FacetCondition { } let fields_ids_map = index.fields_ids_map(rtxn)?; - let faceted_fields = index.faceted_fields_ids(rtxn)?; + let faceted_fields = index.faceted_fields(rtxn)?; let mut ands = None; for either in array { @@ -185,8 +190,8 @@ impl FacetCondition { let mut ors = None; for rule in array { let mut iter = rule.as_ref().splitn(2, ':'); - let key = iter.next().unwrap(); - let value = iter.next().unwrap(); + let key = iter.next().context("missing facet condition key")?; + let value = iter.next().context("missing facet condition value")?; let condition = facet_condition(&fields_ids_map, &faceted_fields, key, value)?; ors = match ors.take() { Some(ors) => Some(Or(Box::new(ors), Box::new(condition))), @@ -203,8 +208,8 @@ impl FacetCondition { }, Either::Right(rule) => { let mut iter = rule.as_ref().splitn(2, ':'); - let key = iter.next().unwrap(); - let value = iter.next().unwrap(); + let key = iter.next().context("missing facet condition key")?; + let value = iter.next().context("missing facet condition value")?; let condition = facet_condition(&fields_ids_map, &faceted_fields, key, value)?; ands = match ands.take() { Some(ands) => Some(And(Box::new(ands), Box::new(condition))), From d91d321129620b5ddf6c97acdbe5adb65a66a2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 14:32:30 +0100 Subject: [PATCH 23/32] Introduce some constants to the FacetDistribution struct and settings --- src/search/facet/facet_distribution.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 06c84bf17..8bab2cb62 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -10,6 +10,18 @@ use crate::heed_codec::facet::{FieldDocIdFacetStringCodec, FieldDocIdFacetF64Cod use crate::search::facet::{FacetIter, FacetRange}; use crate::{Index, FieldId}; +/// The default number of values by facets that will +/// be fetched from the key-value store. +const DEFAULT_VALUES_BY_FACET: usize = 100; + +/// The hard limit in the number of values by facets that will be fetched from +/// the key-value store. Searching for more values could slow down the engine. +const MAX_VALUES_BY_FACET: usize = 1000; + +/// Threshold on the number of candidates that will make +/// the system to choose between one algorithm or another. +const CANDIDATES_THRESHOLD: u64 = 1000; + pub struct FacetDistribution<'a> { facets: Option>, candidates: Option, @@ -20,7 +32,13 @@ pub struct FacetDistribution<'a> { impl<'a> FacetDistribution<'a> { pub fn new(rtxn: &'a heed::RoTxn, index: &'a Index) -> FacetDistribution<'a> { - FacetDistribution { facets: None, candidates: None, max_values_by_facet: 100, rtxn, index } + FacetDistribution { + facets: None, + candidates: None, + max_values_by_facet: DEFAULT_VALUES_BY_FACET, + rtxn, + index, + } } pub fn facets, A: AsRef>(&mut self, names: I) -> &mut Self { @@ -34,7 +52,7 @@ impl<'a> FacetDistribution<'a> { } pub fn max_values_by_facet(&mut self, max: usize) -> &mut Self { - self.max_values_by_facet = cmp::min(max, 1000); + self.max_values_by_facet = cmp::min(max, MAX_VALUES_BY_FACET); self } @@ -45,7 +63,7 @@ impl<'a> FacetDistribution<'a> { ) -> heed::Result> { if let Some(candidates) = self.candidates.as_ref() { - if candidates.len() <= 1000 { + if candidates.len() <= CANDIDATES_THRESHOLD { let mut key_buffer = vec![field_id]; match facet_type { FacetType::Float => { From b52d500fbca32847ebefb557623c611caac9d1d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 14:36:49 +0100 Subject: [PATCH 24/32] Reorder the FacetType enum branching in the facet_value method --- src/search/facet/facet_distribution.rs | 50 +++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 8bab2cb62..490a18229 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -66,6 +66,21 @@ impl<'a> FacetDistribution<'a> { if candidates.len() <= CANDIDATES_THRESHOLD { let mut key_buffer = vec![field_id]; match facet_type { + FacetType::String => { + let mut facet_values = BTreeMap::new(); + for docid in candidates { + key_buffer.truncate(1); + key_buffer.extend_from_slice(&docid.to_be_bytes()); + let iter = self.index.field_id_docid_facet_values + .prefix_iter(self.rtxn, &key_buffer)? + .remap_key_type::(); + for result in iter { + let ((_, _, value), ()) = result?; + *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; + } + } + Ok(facet_values) + }, FacetType::Float => { let mut facet_values = BTreeMap::new(); for docid in candidates { @@ -96,21 +111,6 @@ impl<'a> FacetDistribution<'a> { } Ok(facet_values) }, - FacetType::String => { - let mut facet_values = BTreeMap::new(); - for docid in candidates { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - for result in iter { - let ((_, _, value), ()) = result?; - *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; - } - } - Ok(facet_values) - }, } } else { let iter = match facet_type { @@ -122,14 +122,14 @@ impl<'a> FacetDistribution<'a> { .map(|r| r.map(|((_, v), docids)| (FacetValue::from(v), docids))); Box::new(iter) as Box::> }, - FacetType::Integer => { - let iter = FacetIter::::new_non_reducing( + FacetType::Float => { + let iter = FacetIter::::new_non_reducing( self.rtxn, self.index, field_id, candidates.clone(), )?; Box::new(iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids)))) }, - FacetType::Float => { - let iter = FacetIter::::new_non_reducing( + FacetType::Integer => { + let iter = FacetIter::::new_non_reducing( self.rtxn, self.index, field_id, candidates.clone(), )?; Box::new(iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids)))) @@ -160,16 +160,16 @@ impl<'a> FacetDistribution<'a> { .map(|r| r.map(|((_, v), docids)| (FacetValue::from(v), docids))); Box::new(iter) as Box::> }, - FacetType::Integer => { - let db = db.remap_key_type::(); - let range = FacetRange::::new( + FacetType::Float => { + let db = db.remap_key_type::(); + let range = FacetRange::::new( self.rtxn, db, field_id, 0, Unbounded, Unbounded, )?; Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (FacetValue::from(v), docids)))) }, - FacetType::Float => { - let db = db.remap_key_type::(); - let range = FacetRange::::new( + FacetType::Integer => { + let db = db.remap_key_type::(); + let range = FacetRange::::new( self.rtxn, db, field_id, 0, Unbounded, Unbounded, )?; Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (FacetValue::from(v), docids)))) From 2e00740515a9972088ad0c156a672d9cf4c4bae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 14:41:36 +0100 Subject: [PATCH 25/32] Make sure that we don't iterate throught all string facet values --- src/search/facet/facet_distribution.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 490a18229..970631218 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -63,17 +63,18 @@ impl<'a> FacetDistribution<'a> { ) -> heed::Result> { if let Some(candidates) = self.candidates.as_ref() { - if candidates.len() <= CANDIDATES_THRESHOLD { + if candidates.len() <= CANDIDATES_THRESHOLD || facet_type == FacetType::String { let mut key_buffer = vec![field_id]; match facet_type { FacetType::String => { let mut facet_values = BTreeMap::new(); - for docid in candidates { + for docid in candidates.into_iter().take(CANDIDATES_THRESHOLD as usize) { key_buffer.truncate(1); key_buffer.extend_from_slice(&docid.to_be_bytes()); let iter = self.index.field_id_docid_facet_values .prefix_iter(self.rtxn, &key_buffer)? .remap_key_type::(); + for result in iter { let ((_, _, value), ()) = result?; *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; @@ -89,6 +90,7 @@ impl<'a> FacetDistribution<'a> { let iter = self.index.field_id_docid_facet_values .prefix_iter(self.rtxn, &key_buffer)? .remap_key_type::(); + for result in iter { let ((_, _, value), ()) = result?; *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; @@ -104,6 +106,7 @@ impl<'a> FacetDistribution<'a> { let iter = self.index.field_id_docid_facet_values .prefix_iter(self.rtxn, &key_buffer)? .remap_key_type::(); + for result in iter { let ((_, _, value), ()) = result?; *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; @@ -114,19 +117,13 @@ impl<'a> FacetDistribution<'a> { } } else { let iter = match facet_type { - FacetType::String => { - let db = self.index.facet_field_id_value_docids; - let iter = db - .prefix_iter(self.rtxn, &[field_id])? - .remap_key_type::() - .map(|r| r.map(|((_, v), docids)| (FacetValue::from(v), docids))); - Box::new(iter) as Box::> - }, + FacetType::String => unreachable!(), FacetType::Float => { let iter = FacetIter::::new_non_reducing( self.rtxn, self.index, field_id, candidates.clone(), )?; - Box::new(iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids)))) + let iter = iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids))); + Box::new(iter) as Box::> }, FacetType::Integer => { let iter = FacetIter::::new_non_reducing( From 9c8a654079f82091f3fa1d086a43a58ff28c45f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 14:44:16 +0100 Subject: [PATCH 26/32] Add comments to help read the facet_values branchings --- src/search/facet/facet_distribution.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 970631218..10fe1a3c3 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -63,7 +63,11 @@ impl<'a> FacetDistribution<'a> { ) -> heed::Result> { if let Some(candidates) = self.candidates.as_ref() { + // Classic search, candidates were specified, we must return + // facet values only related to those candidates. if candidates.len() <= CANDIDATES_THRESHOLD || facet_type == FacetType::String { + // There is a small amount of candidates OR we ask for facet string values so we + // decide to iterate over the facet values of each one of them, one by one. let mut key_buffer = vec![field_id]; match facet_type { FacetType::String => { @@ -116,6 +120,8 @@ impl<'a> FacetDistribution<'a> { }, } } else { + // There is too much documents, we use the facet levels to move throught + // the facet values, to find the candidates and values associated. let iter = match facet_type { FacetType::String => unreachable!(), FacetType::Float => { @@ -148,6 +154,8 @@ impl<'a> FacetDistribution<'a> { Ok(facet_values) } } else { + // Placeholder search, a.k.a. no candidates were specified. We iterate throught the + // facet values one by one and iterate on the facet level 0 for numbers. let db = self.index.facet_field_id_value_docids; let iter = match facet_type { FacetType::String => { From 11309ee99c6ad8d6d708a076847db6dfff615eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 14:53:50 +0100 Subject: [PATCH 27/32] Rework the FacetDistribution execute method to use the faceted_fields struct --- src/search/facet/facet_distribution.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 10fe1a3c3..01f7b7205 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -196,22 +196,20 @@ impl<'a> FacetDistribution<'a> { pub fn execute(&self) -> heed::Result>> { let fields_ids_map = self.index.fields_ids_map(self.rtxn)?; - let faceted_fields = self.index.faceted_fields_ids(self.rtxn)?; + let faceted_fields = self.index.faceted_fields(self.rtxn)?; let fields_ids: Vec<_> = match &self.facets { - Some(names) => { - names.iter().filter_map(|n| { - let id = fields_ids_map.id(n)?; - faceted_fields.get(&id).cloned().map(|t| (id, t)) - }).collect() - }, - None => faceted_fields.iter().map(|(id, t)| (*id, *t)).collect(), + Some(names) => names + .iter() + .filter_map(|n| faceted_fields.get(n).map(|t| (n.to_string(), *t))) + .collect(), + None => faceted_fields.into_iter().collect(), }; let mut facets_values = BTreeMap::new(); - for (fid, ftype) in fields_ids { - let facet_name = fields_ids_map.name(fid).unwrap(); + for (name, ftype) in fields_ids { + let fid = fields_ids_map.id(&name).unwrap(); let values = self.facet_values(fid, ftype)?; - facets_values.insert(facet_name.to_string(), values); + facets_values.insert(name, values); } Ok(facets_values) From a3e3bebed7d2f07872185b32d57d5eb5bdfecc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 18:29:54 +0100 Subject: [PATCH 28/32] Rework the FacetDistribution execute method to use the faceted_fields struct --- src/search/facet/facet_distribution.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 01f7b7205..fc3c72853 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -2,6 +2,7 @@ use std::collections::{HashSet, BTreeMap}; use std::ops::Bound::Unbounded; use std::{cmp, fmt}; +use anyhow::Context; use roaring::RoaringBitmap; use crate::facet::{FacetType, FacetValue}; @@ -194,7 +195,7 @@ impl<'a> FacetDistribution<'a> { } } - pub fn execute(&self) -> heed::Result>> { + pub fn execute(&self) -> anyhow::Result>> { let fields_ids_map = self.index.fields_ids_map(self.rtxn)?; let faceted_fields = self.index.faceted_fields(self.rtxn)?; let fields_ids: Vec<_> = match &self.facets { @@ -207,7 +208,9 @@ impl<'a> FacetDistribution<'a> { let mut facets_values = BTreeMap::new(); for (name, ftype) in fields_ids { - let fid = fields_ids_map.id(&name).unwrap(); + let fid = fields_ids_map.id(&name).with_context(|| { + format!("missing field name {:?} from the fields id map", name) + })?; let values = self.facet_values(fid, ftype)?; facets_values.insert(name, values); } From b41bf586580f1cce26b30768f7c26bd7bb02f8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 16:13:49 +0100 Subject: [PATCH 29/32] Split the FacetDistribution facet_values method into three --- src/search/facet/facet_distribution.rs | 278 ++++++++++++++----------- 1 file changed, 154 insertions(+), 124 deletions(-) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index fc3c72853..4a650b9e6 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -57,6 +57,155 @@ impl<'a> FacetDistribution<'a> { self } + /// There is a small amount of candidates OR we ask for facet string values so we + /// decide to iterate over the facet values of each one of them, one by one. + fn facet_values_from_documents( + &self, + field_id: FieldId, + facet_type: FacetType, + candidates: &RoaringBitmap, + ) -> heed::Result> + { + let mut key_buffer = vec![field_id]; + match facet_type { + FacetType::String => { + let mut facet_values = BTreeMap::new(); + for docid in candidates.into_iter().take(CANDIDATES_THRESHOLD as usize) { + key_buffer.truncate(1); + key_buffer.extend_from_slice(&docid.to_be_bytes()); + let iter = self.index.field_id_docid_facet_values + .prefix_iter(self.rtxn, &key_buffer)? + .remap_key_type::(); + + for result in iter { + let ((_, _, value), ()) = result?; + *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; + } + } + Ok(facet_values) + }, + FacetType::Float => { + let mut facet_values = BTreeMap::new(); + for docid in candidates { + key_buffer.truncate(1); + key_buffer.extend_from_slice(&docid.to_be_bytes()); + let iter = self.index.field_id_docid_facet_values + .prefix_iter(self.rtxn, &key_buffer)? + .remap_key_type::(); + + for result in iter { + let ((_, _, value), ()) = result?; + *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; + } + } + Ok(facet_values) + }, + FacetType::Integer => { + let mut facet_values = BTreeMap::new(); + for docid in candidates { + key_buffer.truncate(1); + key_buffer.extend_from_slice(&docid.to_be_bytes()); + let iter = self.index.field_id_docid_facet_values + .prefix_iter(self.rtxn, &key_buffer)? + .remap_key_type::(); + + for result in iter { + let ((_, _, value), ()) = result?; + *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; + } + } + Ok(facet_values) + }, + } + } + + /// There is too much documents, we use the facet levels to move throught + /// the facet values, to find the candidates and values associated. + fn facet_values_from_facet_levels( + &self, + field_id: FieldId, + facet_type: FacetType, + candidates: &RoaringBitmap, + ) -> heed::Result> + { + let iter = match facet_type { + FacetType::String => unreachable!(), + FacetType::Float => { + let iter = FacetIter::::new_non_reducing( + self.rtxn, self.index, field_id, candidates.clone(), + )?; + let iter = iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids))); + Box::new(iter) as Box::> + }, + FacetType::Integer => { + let iter = FacetIter::::new_non_reducing( + self.rtxn, self.index, field_id, candidates.clone(), + )?; + Box::new(iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids)))) + }, + }; + + let mut facet_values = BTreeMap::new(); + for result in iter { + let (value, mut docids) = result?; + docids.intersect_with(candidates); + if !docids.is_empty() { + facet_values.insert(value, docids.len()); + } + if facet_values.len() == self.max_values_by_facet { + break; + } + } + + Ok(facet_values) + } + + /// Placeholder search, a.k.a. no candidates were specified. We iterate throught the + /// facet values one by one and iterate on the facet level 0 for numbers. + fn facet_values_from_raw_facet_database( + &self, + field_id: FieldId, + facet_type: FacetType, + ) -> heed::Result> + { + let db = self.index.facet_field_id_value_docids; + let level = 0; + let iter = match facet_type { + FacetType::String => { + let iter = db + .prefix_iter(self.rtxn, &[field_id])? + .remap_key_type::() + .map(|r| r.map(|((_, v), docids)| (FacetValue::from(v), docids))); + Box::new(iter) as Box::> + }, + FacetType::Float => { + let db = db.remap_key_type::(); + let range = FacetRange::::new( + self.rtxn, db, field_id, level, Unbounded, Unbounded, + )?; + Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (FacetValue::from(v), docids)))) + }, + FacetType::Integer => { + let db = db.remap_key_type::(); + let range = FacetRange::::new( + self.rtxn, db, field_id, level, Unbounded, Unbounded, + )?; + Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (FacetValue::from(v), docids)))) + }, + }; + + let mut facet_values = BTreeMap::new(); + for result in iter { + let (value, docids) = result?; + facet_values.insert(value, docids.len()); + if facet_values.len() == self.max_values_by_facet { + break; + } + } + + Ok(facet_values) + } + fn facet_values( &self, field_id: FieldId, @@ -64,134 +213,15 @@ impl<'a> FacetDistribution<'a> { ) -> heed::Result> { if let Some(candidates) = self.candidates.as_ref() { - // Classic search, candidates were specified, we must return - // facet values only related to those candidates. + // Classic search, candidates were specified, we must return facet values only related + // to those candidates. We also enter here for facet strings for performance reasons. if candidates.len() <= CANDIDATES_THRESHOLD || facet_type == FacetType::String { - // There is a small amount of candidates OR we ask for facet string values so we - // decide to iterate over the facet values of each one of them, one by one. - let mut key_buffer = vec![field_id]; - match facet_type { - FacetType::String => { - let mut facet_values = BTreeMap::new(); - for docid in candidates.into_iter().take(CANDIDATES_THRESHOLD as usize) { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - - for result in iter { - let ((_, _, value), ()) = result?; - *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; - } - } - Ok(facet_values) - }, - FacetType::Float => { - let mut facet_values = BTreeMap::new(); - for docid in candidates { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - - for result in iter { - let ((_, _, value), ()) = result?; - *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; - } - } - Ok(facet_values) - }, - FacetType::Integer => { - let mut facet_values = BTreeMap::new(); - for docid in candidates { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - - for result in iter { - let ((_, _, value), ()) = result?; - *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; - } - } - Ok(facet_values) - }, - } + self.facet_values_from_documents(field_id, facet_type, candidates) } else { - // There is too much documents, we use the facet levels to move throught - // the facet values, to find the candidates and values associated. - let iter = match facet_type { - FacetType::String => unreachable!(), - FacetType::Float => { - let iter = FacetIter::::new_non_reducing( - self.rtxn, self.index, field_id, candidates.clone(), - )?; - let iter = iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids))); - Box::new(iter) as Box::> - }, - FacetType::Integer => { - let iter = FacetIter::::new_non_reducing( - self.rtxn, self.index, field_id, candidates.clone(), - )?; - Box::new(iter.map(|r| r.map(|(v, docids)| (FacetValue::from(v), docids)))) - }, - }; - - let mut facet_values = BTreeMap::new(); - for result in iter { - let (value, mut docids) = result?; - docids.intersect_with(candidates); - if !docids.is_empty() { - facet_values.insert(value, docids.len()); - } - if facet_values.len() == self.max_values_by_facet { - break; - } - } - - Ok(facet_values) + self.facet_values_from_facet_levels(field_id, facet_type, candidates) } } else { - // Placeholder search, a.k.a. no candidates were specified. We iterate throught the - // facet values one by one and iterate on the facet level 0 for numbers. - let db = self.index.facet_field_id_value_docids; - let iter = match facet_type { - FacetType::String => { - let iter = db - .prefix_iter(self.rtxn, &[field_id])? - .remap_key_type::() - .map(|r| r.map(|((_, v), docids)| (FacetValue::from(v), docids))); - Box::new(iter) as Box::> - }, - FacetType::Float => { - let db = db.remap_key_type::(); - let range = FacetRange::::new( - self.rtxn, db, field_id, 0, Unbounded, Unbounded, - )?; - Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (FacetValue::from(v), docids)))) - }, - FacetType::Integer => { - let db = db.remap_key_type::(); - let range = FacetRange::::new( - self.rtxn, db, field_id, 0, Unbounded, Unbounded, - )?; - Box::new(range.map(|r| r.map(|((_, _, v, _), docids)| (FacetValue::from(v), docids)))) - }, - }; - - let mut facet_values = BTreeMap::new(); - for result in iter { - let (value, docids) = result?; - facet_values.insert(value, docids.len()); - if facet_values.len() == self.max_values_by_facet { - break; - } - } - - Ok(facet_values) + self.facet_values_from_raw_facet_database(field_id, facet_type) } } From b6e91291fbf7227f087e5e041f68f31108f3d124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 16:31:57 +0100 Subject: [PATCH 30/32] Add a comment to explain Serialize on FacetValue is implemented by hand --- src/facet/facet_value.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/facet/facet_value.rs b/src/facet/facet_value.rs index 9630c6634..f311ca3dd 100644 --- a/src/facet/facet_value.rs +++ b/src/facet/facet_value.rs @@ -38,6 +38,8 @@ impl From for FacetValue { } } +/// We implement Serialize ourselves because we need to always serialize it as a string, +/// JSON object keys must be strings not numbers. impl Serialize for FacetValue { fn serialize(&self, serializer: S) -> Result where From f5f4438b4351c02cc5414e5e88807657431c4a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Wed, 27 Jan 2021 18:31:09 +0100 Subject: [PATCH 31/32] Remove the duplicated code inside the facet_values_from_documents method --- src/search/facet/facet_distribution.rs | 80 ++++++++++++-------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/src/search/facet/facet_distribution.rs b/src/search/facet/facet_distribution.rs index 4a650b9e6..afa4f2a5a 100644 --- a/src/search/facet/facet_distribution.rs +++ b/src/search/facet/facet_distribution.rs @@ -3,13 +3,14 @@ use std::ops::Bound::Unbounded; use std::{cmp, fmt}; use anyhow::Context; +use heed::BytesDecode; use roaring::RoaringBitmap; use crate::facet::{FacetType, FacetValue}; use crate::heed_codec::facet::{FacetValueStringCodec, FacetLevelValueF64Codec, FacetLevelValueI64Codec}; use crate::heed_codec::facet::{FieldDocIdFacetStringCodec, FieldDocIdFacetF64Codec, FieldDocIdFacetI64Codec}; use crate::search::facet::{FacetIter, FacetRange}; -use crate::{Index, FieldId}; +use crate::{Index, FieldId, DocumentId}; /// The default number of values by facets that will /// be fetched from the key-value store. @@ -66,55 +67,46 @@ impl<'a> FacetDistribution<'a> { candidates: &RoaringBitmap, ) -> heed::Result> { - let mut key_buffer = vec![field_id]; + fn fetch_facet_values<'t, KC, K: 't>( + index: &Index, + rtxn: &'t heed::RoTxn, + field_id: FieldId, + candidates: &RoaringBitmap, + ) -> heed::Result> + where + KC: BytesDecode<'t, DItem = (FieldId, DocumentId, K)>, + K: Into, + { + let mut facet_values = BTreeMap::new(); + let mut key_buffer = vec![field_id]; + + for docid in candidates.into_iter().take(CANDIDATES_THRESHOLD as usize) { + key_buffer.truncate(1); + key_buffer.extend_from_slice(&docid.to_be_bytes()); + let iter = index.field_id_docid_facet_values + .prefix_iter(rtxn, &key_buffer)? + .remap_key_type::(); + + for result in iter { + let ((_, _, value), ()) = result?; + *facet_values.entry(value.into()).or_insert(0) += 1; + } + } + + Ok(facet_values) + } + + let index = self.index; + let rtxn = self.rtxn; match facet_type { FacetType::String => { - let mut facet_values = BTreeMap::new(); - for docid in candidates.into_iter().take(CANDIDATES_THRESHOLD as usize) { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - - for result in iter { - let ((_, _, value), ()) = result?; - *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; - } - } - Ok(facet_values) + fetch_facet_values::(index, rtxn, field_id, candidates) }, FacetType::Float => { - let mut facet_values = BTreeMap::new(); - for docid in candidates { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - - for result in iter { - let ((_, _, value), ()) = result?; - *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; - } - } - Ok(facet_values) + fetch_facet_values::(index, rtxn, field_id, candidates) }, FacetType::Integer => { - let mut facet_values = BTreeMap::new(); - for docid in candidates { - key_buffer.truncate(1); - key_buffer.extend_from_slice(&docid.to_be_bytes()); - let iter = self.index.field_id_docid_facet_values - .prefix_iter(self.rtxn, &key_buffer)? - .remap_key_type::(); - - for result in iter { - let ((_, _, value), ()) = result?; - *facet_values.entry(FacetValue::from(value)).or_insert(0) += 1; - } - } - Ok(facet_values) + fetch_facet_values::(index, rtxn, field_id, candidates) }, } } From 14ae01a6c9a80fa43d7c8cf53f53801cae1949ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Mon, 1 Feb 2021 18:10:57 +0100 Subject: [PATCH 32/32] Fix some typos in error messages --- src/search/facet/facet_condition.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search/facet/facet_condition.rs b/src/search/facet/facet_condition.rs index 8e7d16a83..578344d37 100644 --- a/src/search/facet/facet_condition.rs +++ b/src/search/facet/facet_condition.rs @@ -161,10 +161,10 @@ impl FacetCondition { ) -> anyhow::Result { let fid = fields_ids_map.id(key).with_context(|| { - format!("{:?} must isn't part of the fields ids map", key) + format!("{:?} isn't present in the fields ids map", key) })?; let ftype = faceted_fields.get(key).copied().with_context(|| { - format!("{:?} must isn't a faceted field", key) + format!("{:?} isn't a faceted field", key) })?; let (neg, value) = match value.trim().strip_prefix('-') { Some(value) => (true, value.trim()),