mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-11-29 16:24:26 +01:00
logic skeleton for filter and parser
This commit is contained in:
parent
6db6b40659
commit
66568a913c
1992
Cargo.lock
generated
1992
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@ members = [
|
|||||||
"meilisearch-schema",
|
"meilisearch-schema",
|
||||||
"meilisearch-tokenizer",
|
"meilisearch-tokenizer",
|
||||||
"meilisearch-types",
|
"meilisearch-types",
|
||||||
|
"meilisearch-filters",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
15
meilisearch-filters/Cargo.toml
Normal file
15
meilisearch-filters/Cargo.toml
Normal 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"
|
||||||
|
|
23
meilisearch-filters/src/filter.rs
Normal file
23
meilisearch-filters/src/filter.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
6
meilisearch-filters/src/lib.rs
Normal file
6
meilisearch-filters/src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
extern crate pest;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate pest_derive;
|
||||||
|
|
||||||
|
mod parser;
|
||||||
|
pub mod filter;
|
15
meilisearch-filters/src/parser/grammar.pest
Normal file
15
meilisearch-filters/src/parser/grammar.pest
Normal 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 = _{ " " }
|
18
meilisearch-filters/src/parser/mod.rs
Normal file
18
meilisearch-filters/src/parser/mod.rs
Normal 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;
|
100
meilisearch-filters/src/parser/operation.rs
Normal file
100
meilisearch-filters/src/parser/operation.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user