logic skeleton for filter and parser

This commit is contained in:
mposmta 2020-03-30 18:10:41 +02:00
parent 6db6b40659
commit 66568a913c
8 changed files with 1260 additions and 910 deletions

1992
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ members = [
"meilisearch-schema", "meilisearch-schema",
"meilisearch-tokenizer", "meilisearch-tokenizer",
"meilisearch-types", "meilisearch-types",
"meilisearch-filters",
] ]
[profile.release] [profile.release]

View File

@ -0,0 +1,15 @@
[package]
name = "meilisearch-filters"
version = "0.9.0"
authors = ["mposmta <postma.marin@protonmail.com>"]
edition = "2018"
license = "MIT"
[dependencies]
lazy_static = "1.4.0"
meilisearch-core = { path = "../meilisearch-core", version = "0.9.0" }
pest = "2.0"
pest_derive = "2.0"
heed = "0.6.1"

View File

@ -0,0 +1,23 @@
use crate::parser::Operation;
use meilisearch_core::{DocumentId, Schema, MainT };
use heed::RoTxn;
pub struct Filter<'r> {
reader: &'r RoTxn<MainT>,
operation: Box<Operation>,
}
impl<'r> Filter<'r> {
pub fn new<T: AsRef<str>>(expr: T, schema: &Schema, reader: &'r RoTxn<MainT>) -> Result<Self, Box<dyn std::error::Error>> {
let operation = Box::new(Operation::parse_with_schema(expr, schema)?);
Ok( Self {
reader,
operation,
})
}
pub fn test(&self, _document_id: &DocumentId) -> Result<bool, Box<dyn std::error::Error>> {
unimplemented!()
}
}

View File

@ -0,0 +1,6 @@
extern crate pest;
#[macro_use]
extern crate pest_derive;
mod parser;
pub mod filter;

View File

@ -0,0 +1,15 @@
key = @{ASCII_ALPHANUMERIC+}
value = @{ASCII_ALPHANUMERIC*}
query = {key ~ ":" ~ value}
prgm = {SOI ~ expr ~ EOI}
expr = _{ term ~ (operation ~ term)* }
term = _{query | "(" ~ expr ~ ")" | not}
operation = _{ and | or }
and = {"AND"}
or = {"OR"}
not = {"NOT" ~ term}
WHITESPACE = _{ " " }

View File

@ -0,0 +1,18 @@
pub mod operation;
use lazy_static::lazy_static;
use pest::prec_climber::{Operator, Assoc, PrecClimber};
pub use operation::Operation;
lazy_static! {
static ref PREC_CLIMBER: PrecClimber<Rule> = {
use Assoc::*;
use Rule::*;
pest::prec_climber::PrecClimber::new(vec![Operator::new(or, Left), Operator::new(and, Left)])
};
}
#[derive(Parser)]
#[grammar = "parser/grammar.pest"]
pub struct FilterParser;

View File

@ -0,0 +1,100 @@
use super::{FilterParser, Rule, PREC_CLIMBER};
use pest::{
iterators::{Pair, Pairs},
Parser,
};
use std::convert::From;
use std::fmt;
use meilisearch_core::Schema;
pub enum Query {
Contains { field: String, value: String },
IsEqual { field: String, value: String },
IsLower { field: String, value: String },
}
impl fmt::Debug for Query {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Contains { field, value } => write!(f, "{}:{}", field, value),
_ => todo!(),
}
}
}
impl From<Pair<'_, Rule>> for Query {
fn from(item: Pair<Rule>) -> Self {
let mut items = item.into_inner();
let key = items.next().unwrap();
// do additional parsing here and get the correct query type
let value = items.next().unwrap();
Self::Contains {
field: key.as_str().to_owned(),
value: value.as_str().to_owned(),
}
}
}
#[derive(Debug)]
pub struct Span(usize, usize);
impl Span {
pub fn merge(&self, other: &Span) -> Self {
let start = if self.0 > other.0 { other.0 } else { self.0 };
let end = if self.0 < other.0 { other.0 } else { self.0 };
Span(start, end)
}
}
impl From<pest::Span<'_>> for Span {
fn from(other: pest::Span<'_>) -> Self {
Span(other.start(), other.end())
}
}
#[derive(Debug)]
pub enum Operation {
Query(Query, Span),
Or(Box<Operation>, Box<Operation>, Span),
And(Box<Operation>, Box<Operation>, Span),
Not(Box<Operation>, Span),
}
impl Operation {
pub fn as_span<'a>(&'a self) -> &'a Span {
use Operation::*;
match self {
Query(_, span) | Or(_, _, span) | And(_, _, span) | Not(_, span) => span,
}
}
}
fn eval(expression: Pairs<Rule>) -> Operation {
PREC_CLIMBER.climb(
expression,
|pair: Pair<Rule>| {
let span = Span::from(pair.as_span());
match pair.as_rule() {
Rule::query => Operation::Query(Query::from(pair), span),
Rule::prgm => eval(pair.into_inner()),
Rule::not => Operation::Not(Box::new(eval(pair.into_inner())), span),
_ => unreachable!(),
}
},
|lhs: Operation, op: Pair<Rule>, rhs: Operation| {
let span = lhs.as_span().merge(rhs.as_span());
match op.as_rule() {
Rule::or => Operation::Or(Box::new(lhs), Box::new(rhs), span),
Rule::and => Operation::And(Box::new(lhs), Box::new(rhs), span),
_ => unreachable!(),
}
},
)
}
impl Operation {
pub fn parse_with_schema<T: AsRef<str>>(_expr: T, _schema: &Schema) -> Result<Self, Box<dyn std::error::Error>> {
unimplemented!()
}
}