Skip to content

Commit f7ed0b7

Browse files
committed
allow mixing occur and bool in query
1 parent 86b63db commit f7ed0b7

File tree

2 files changed

+117
-44
lines changed

2 files changed

+117
-44
lines changed

query-grammar/src/query_grammar.rs

Lines changed: 113 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ fn operand_occur_leaf_infallible(
731731
))(i)
732732
}
733733

734-
#[derive(Clone, Copy, Debug)]
734+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
735735
enum BinaryOperand {
736736
Or,
737737
And,
@@ -770,54 +770,118 @@ fn aggregate_binary_expressions(
770770
}
771771

772772
fn aggregate_infallible_expressions(
773-
leafs: Vec<(Option<BinaryOperand>, Option<Occur>, Option<UserInputAst>)>,
773+
input_leafs: Vec<(Option<BinaryOperand>, Option<Occur>, Option<UserInputAst>)>,
774774
) -> (UserInputAst, ErrorList) {
775-
let mut leafs: Vec<(_, _, UserInputAst)> = leafs
775+
let mut err = Vec::new();
776+
let mut leafs: Vec<(_, _, UserInputAst)> = input_leafs
776777
.into_iter()
777778
.filter_map(|(operand, occur, ast)| ast.map(|ast| (operand, occur, ast)))
778779
.collect();
779780
if leafs.is_empty() {
780-
// TODO should this produce an error?
781-
return (UserInputAst::empty_query(), Vec::new());
781+
return (UserInputAst::empty_query(), err);
782782
}
783783

784-
// if no occur => use binary operand (default = or)
785-
// if no binary opearnd => use occur
784+
let use_operand = leafs.iter().any(|(operand, _, _)| operand.is_some());
785+
let all_operand = leafs
786+
.iter()
787+
.skip(1)
788+
.all(|(operand, _, _)| operand.is_some());
789+
let early_operand = leafs
790+
.iter()
791+
.take(1)
792+
.all(|(operand, _, _)| operand.is_some());
793+
let use_occur = leafs.iter().any(|(_, occur, _)| occur.is_some());
794+
795+
if use_operand && use_occur {
796+
err.push(LenientErrorInternal {
797+
pos: 0,
798+
message: "Use of mixed occur and boolean operator".to_string(),
799+
});
800+
}
801+
802+
if use_operand && !all_operand {
803+
err.push(LenientErrorInternal {
804+
pos: 0,
805+
message: "Missing boolean operator".to_string(),
806+
});
807+
}
808+
809+
if early_operand {
810+
err.push(LenientErrorInternal {
811+
pos: 0,
812+
message: "Found unexpeted boolean operator before term".to_string(),
813+
});
814+
}
786815

787-
if leafs.len() == 1 {
788-
match leafs.remove(0) {
789-
(_, occur, ast) if occur != Some(Occur::MustNot) => return (ast, Vec::new()),
790-
(_, occur, ast) => return (UserInputAst::Clause(vec![(occur, ast)]), Vec::new()),
816+
let mut clauses: Vec<Vec<(Option<Occur>, UserInputAst)>> = vec![];
817+
for ((prev_operator, occur, ast), (next_operator, _, _)) in
818+
leafs.iter().zip(leafs.iter().skip(1))
819+
{
820+
match prev_operator {
821+
Some(BinaryOperand::And) => {
822+
if let Some(last) = clauses.last_mut() {
823+
last.push((occur.or(Some(Occur::Must)), ast.clone()));
824+
} else {
825+
let mut last = Vec::new();
826+
last.push((occur.or(Some(Occur::Must)), ast.clone()));
827+
clauses.push(last);
828+
}
829+
}
830+
Some(BinaryOperand::Or) => {
831+
let default_op = match next_operator {
832+
Some(BinaryOperand::And) => Some(Occur::Must),
833+
_ => Some(Occur::Should),
834+
};
835+
clauses.push(vec![(occur.or(default_op), ast.clone())]);
836+
}
837+
None => {
838+
let default_op = match next_operator {
839+
Some(BinaryOperand::And) => Some(Occur::Must),
840+
Some(BinaryOperand::Or) => Some(Occur::Should),
841+
None => None,
842+
};
843+
clauses.push(vec![(occur.or(default_op), ast.clone())])
844+
}
791845
}
792846
}
793847

794-
if leafs.iter().all(|(operand, _, _)| operand.is_none()) {
795-
let clauses = leafs
796-
.into_iter()
797-
.map(|(_, occur, ast)| (occur, ast))
798-
.collect();
799-
return (UserInputAst::Clause(clauses), Vec::new());
848+
// leaf isn't empty, so we can unwrap
849+
let (last_operator, last_occur, last_ast) = leafs.pop().unwrap();
850+
match last_operator {
851+
Some(BinaryOperand::And) => {
852+
if let Some(last) = clauses.last_mut() {
853+
last.push((last_occur.or(Some(Occur::Must)), last_ast));
854+
} else {
855+
let mut last = Vec::new();
856+
last.push((last_occur.or(Some(Occur::Must)), last_ast));
857+
clauses.push(last);
858+
}
859+
}
860+
Some(BinaryOperand::Or) => {
861+
clauses.push(vec![(last_occur.or(Some(Occur::Should)), last_ast)]);
862+
}
863+
None => clauses.push(vec![(last_occur, last_ast)]),
800864
}
801865

802-
if leafs.iter().all(|(_, occur, _)| occur.is_none()) {
803-
let first = leafs.remove(0).2;
804-
let errors = if leafs.iter().any(|(operand, _, _)| operand.is_none()) {
805-
vec![LenientErrorInternal {
806-
pos: 0, // we don't really know the position
807-
message: "binary expression with missing binary operator".to_string(),
808-
}]
866+
if clauses.len() == 1 {
867+
let mut clause = clauses.pop().unwrap();
868+
if clause.len() == 1 && clause[0].0 != Some(Occur::MustNot) {
869+
(clause.pop().unwrap().1, err)
809870
} else {
810-
Vec::new()
811-
};
812-
let rest = leafs
813-
.into_iter()
814-
.map(|(operand, _, ast)| (operand.unwrap_or(BinaryOperand::Or), ast))
815-
.collect();
816-
return (aggregate_binary_expressions(first, rest), errors);
817-
}
871+
(UserInputAst::Clause(clause), err)
872+
}
873+
} else {
874+
let mut final_clauses: Vec<(Option<Occur>, UserInputAst)> = Vec::new();
875+
for mut sub_clauses in clauses {
876+
if sub_clauses.len() == 1 {
877+
final_clauses.push(sub_clauses.pop().unwrap());
878+
} else {
879+
final_clauses.push((Some(Occur::Should), UserInputAst::Clause(sub_clauses)));
880+
}
881+
}
818882

819-
// TODO a mix of both isn't that easy, will do later
820-
todo!()
883+
(UserInputAst::Clause(final_clauses), err)
884+
}
821885
}
822886

823887
fn operand_leaf(i: &str) -> IResult<&str, (BinaryOperand, UserInputAst)> {
@@ -1048,12 +1112,21 @@ mod test {
10481112
test_parse_query_to_ast_helper("a OR b", "(?a ?b)");
10491113
test_parse_query_to_ast_helper("a OR b AND c", "(?a ?(+b +c))");
10501114
test_parse_query_to_ast_helper("a AND b AND c", "(+a +b +c)");
1051-
test_is_parse_err("a OR b aaa", "(?a ?b ?aaa)");
1052-
test_is_parse_err("a AND b aaa", "(?(+a +b) ?aaa)");
1053-
test_is_parse_err("aaa a OR b ", "(?aaa ?a ?b)");
1054-
test_is_parse_err("aaa ccc a OR b ", "(?aaa ?ccc ?a ?b)");
1055-
test_is_parse_err("aaa a AND b ", "(?aaa ?(+a +b))");
1056-
test_is_parse_err("aaa ccc a AND b ", "(?aaa ?ccc ?(+a +b))");
1115+
test_is_parse_err("a OR b aaa", "(?a ?b *aaa)");
1116+
test_is_parse_err("a AND b aaa", "(?(+a +b) *aaa)");
1117+
test_is_parse_err("aaa a OR b ", "(*aaa ?a ?b)");
1118+
test_is_parse_err("aaa ccc a OR b ", "(*aaa *ccc ?a ?b)");
1119+
test_is_parse_err("aaa a AND b ", "(*aaa ?(+a +b))");
1120+
test_is_parse_err("aaa ccc a AND b ", "(*aaa *ccc ?(+a +b))");
1121+
}
1122+
1123+
#[test]
1124+
fn test_parse_mixed_bool_occur() {
1125+
test_is_parse_err("a OR b +aaa", "(?a ?b +aaa)");
1126+
test_is_parse_err("a AND b -aaa", "(?(+a +b) -aaa)");
1127+
test_is_parse_err("+a OR +b aaa", "(+a +b *aaa)");
1128+
test_is_parse_err("-a AND -b aaa", "(?(-a -b) *aaa)");
1129+
test_is_parse_err("-aaa +ccc -a OR b ", "(-aaa +ccc -a ?b)");
10571130
}
10581131

10591132
#[test]

query-grammar/src/user_input_ast.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt::{Debug, Formatter};
33

44
use crate::Occur;
55

6-
#[derive(PartialEq)]
6+
#[derive(PartialEq, Clone)]
77
pub enum UserInputLeaf {
88
Literal(UserInputLiteral),
99
All,
@@ -85,7 +85,7 @@ pub enum Delimiter {
8585
None,
8686
}
8787

88-
#[derive(PartialEq)]
88+
#[derive(PartialEq, Clone)]
8989
pub struct UserInputLiteral {
9090
pub field_name: Option<String>,
9191
pub phrase: String,
@@ -123,7 +123,7 @@ impl fmt::Debug for UserInputLiteral {
123123
}
124124
}
125125

126-
#[derive(PartialEq, Debug)]
126+
#[derive(PartialEq, Debug, Clone)]
127127
pub enum UserInputBound {
128128
Inclusive(String),
129129
Exclusive(String),
@@ -158,7 +158,7 @@ impl UserInputBound {
158158
}
159159
}
160160

161-
#[derive(PartialEq)]
161+
#[derive(PartialEq, Clone)]
162162
pub enum UserInputAst {
163163
Clause(Vec<(Option<Occur>, UserInputAst)>),
164164
Leaf(Box<UserInputLeaf>),

0 commit comments

Comments
 (0)