Support a basic version of the string facet query system

This commit is contained in:
Clément Renault 2020-11-20 12:59:29 +01:00
parent 498f0d8539
commit c52d09d5b1
No known key found for this signature in database
GPG Key ID: 92ADA4E935E71FA4
3 changed files with 68 additions and 20 deletions

View File

@ -5,19 +5,21 @@ use std::str::FromStr;
use anyhow::{bail, ensure, Context};
use heed::types::{ByteSlice, DecodeIgnore};
use itertools::Itertools;
use log::debug;
use num_traits::Bounded;
use roaring::RoaringBitmap;
use crate::facet::FacetType;
use crate::heed_codec::facet::FacetValueStringCodec;
use crate::heed_codec::facet::{FacetLevelValueI64Codec, FacetLevelValueF64Codec};
use crate::{Index, CboRoaringBitmapCodec};
use self::FacetCondition::*;
use self::FacetOperator::*;
use self::FacetNumberOperator::*;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FacetOperator<T> {
pub enum FacetNumberOperator<T> {
GreaterThan(T),
GreaterThanOrEqual(T),
LowerThan(T),
@ -26,11 +28,17 @@ pub enum FacetOperator<T> {
Between(T, T),
}
#[derive(Debug, Clone, PartialEq)]
pub enum FacetStringOperator {
Equal(String),
}
// TODO also support ANDs, ORs, NOTs.
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub enum FacetCondition {
OperatorI64(u8, FacetOperator<i64>),
OperatorF64(u8, FacetOperator<f64>),
OperatorI64(u8, FacetNumberOperator<i64>),
OperatorF64(u8, FacetNumberOperator<f64>),
OperatorString(u8, FacetStringOperator),
}
impl FacetCondition {
@ -55,15 +63,34 @@ impl FacetCondition {
let field_type = faceted_fields.get(&field_id).with_context(|| format!("field {} is not faceted", field_name))?;
match field_type {
FacetType::Integer => Self::parse_condition(iter).map(|op| Some(OperatorI64(field_id, op))),
FacetType::Float => Self::parse_condition(iter).map(|op| Some(OperatorF64(field_id, op))),
FacetType::String => bail!("invalid facet type"),
FacetType::Integer => Self::parse_number_condition(iter).map(|op| Some(OperatorI64(field_id, op))),
FacetType::Float => Self::parse_number_condition(iter).map(|op| Some(OperatorF64(field_id, op))),
FacetType::String => Self::parse_string_condition(iter).map(|op| Some(OperatorString(field_id, op))),
}
}
fn parse_condition<'a, T: FromStr>(
fn parse_string_condition<'a>(
mut iter: impl Iterator<Item=&'a str>,
) -> anyhow::Result<FacetOperator<T>>
) -> anyhow::Result<FacetStringOperator>
{
match iter.next() {
Some("=") | Some(":") => {
match iter.next() {
Some(q @ "\"") | Some(q @ "\'") => {
let string: String = iter.take_while(|&c| c != q).intersperse(" ").collect();
Ok(FacetStringOperator::Equal(string.to_lowercase()))
},
Some(param) => Ok(FacetStringOperator::Equal(param.to_lowercase())),
None => bail!("missing parameter"),
}
},
_ => bail!("invalid facet string operator"),
}
}
fn parse_number_condition<'a, T: FromStr>(
mut iter: impl Iterator<Item=&'a str>,
) -> anyhow::Result<FacetNumberOperator<T>>
where T::Err: Send + Sync + StdError + 'static,
{
match iter.next() {
@ -201,11 +228,11 @@ impl FacetCondition {
Ok(())
}
fn evaluate_operator<'t, T: 't, KC>(
fn evaluate_number_operator<'t, T: 't, KC>(
rtxn: &'t heed::RoTxn,
db: heed::Database<ByteSlice, CboRoaringBitmapCodec>,
field_id: u8,
operator: FacetOperator<T>,
operator: FacetNumberOperator<T>,
) -> anyhow::Result<RoaringBitmap>
where
T: Copy + PartialEq + PartialOrd + Bounded + Debug,
@ -241,19 +268,40 @@ impl FacetCondition {
}
}
fn evaluate_string_operator(
rtxn: &heed::RoTxn,
db: heed::Database<FacetValueStringCodec, CboRoaringBitmapCodec>,
field_id: u8,
operator: &FacetStringOperator,
) -> anyhow::Result<RoaringBitmap>
{
match operator {
FacetStringOperator::Equal(string) => {
match db.get(rtxn, &(field_id, string))? {
Some(docids) => Ok(docids),
None => Ok(RoaringBitmap::new())
}
}
}
}
pub fn evaluate(
&self,
rtxn: &heed::RoTxn,
db: heed::Database<ByteSlice, CboRoaringBitmapCodec>,
) -> anyhow::Result<RoaringBitmap>
{
match *self {
FacetCondition::OperatorI64(fid, operator) => {
Self::evaluate_operator::<i64, FacetLevelValueI64Codec>(rtxn, db, fid, operator)
match self {
OperatorI64(fid, op) => {
Self::evaluate_number_operator::<i64, FacetLevelValueI64Codec>(rtxn, db, *fid, *op)
},
OperatorF64(fid, op) => {
Self::evaluate_number_operator::<f64, FacetLevelValueF64Codec>(rtxn, db, *fid, *op)
},
OperatorString(fid, op) => {
let db = db.remap_key_type::<FacetValueStringCodec>();
Self::evaluate_string_operator(rtxn, db, *fid, op)
},
FacetCondition::OperatorF64(fid, operator) => {
Self::evaluate_operator::<f64, FacetLevelValueF64Codec>(rtxn, db, fid, operator)
}
}
}
}

View File

@ -156,7 +156,7 @@ impl<'a> Search<'a> {
// We create the original candidates with the facet conditions results.
let facet_db = self.index.facet_field_id_value_docids;
let facet_candidates = match self.facet_condition {
let facet_candidates = match &self.facet_condition {
Some(condition) => Some(condition.evaluate(self.rtxn, facet_db)?),
None => None,
};

View File

@ -586,7 +586,7 @@ fn parse_facet_value(ftype: FacetType, value: &Value) -> anyhow::Result<SmallVec
},
},
Value::String(string) => {
let string = string.trim();
let string = string.trim().to_lowercase();
if string.is_empty() { return Ok(()) }
match ftype {
FacetType::String => {