mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-01-07 05:42:57 +00:00
feat: handle parentheses with unary ops (#4290)
* feat: handle parentheses with unary ops Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * clean up Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * add comment Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * add sqlness test Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * check tokens before convert to RPN Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * add test cases to sqlness Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * fix clippy Signed-off-by: Ruihang Xia <waynestxia@gmail.com> --------- Signed-off-by: Ruihang Xia <waynestxia@gmail.com> Co-authored-by: dennis zhuang <killme2008@gmail.com>
This commit is contained in:
@@ -22,6 +22,7 @@ use crate::function::FunctionRef;
|
|||||||
use crate::scalars::aggregate::{AggregateFunctionMetaRef, AggregateFunctions};
|
use crate::scalars::aggregate::{AggregateFunctionMetaRef, AggregateFunctions};
|
||||||
use crate::scalars::date::DateFunction;
|
use crate::scalars::date::DateFunction;
|
||||||
use crate::scalars::expression::ExpressionFunction;
|
use crate::scalars::expression::ExpressionFunction;
|
||||||
|
use crate::scalars::matches::MatchesFunction;
|
||||||
use crate::scalars::math::MathFunction;
|
use crate::scalars::math::MathFunction;
|
||||||
use crate::scalars::numpy::NumpyFunction;
|
use crate::scalars::numpy::NumpyFunction;
|
||||||
use crate::scalars::timestamp::TimestampFunction;
|
use crate::scalars::timestamp::TimestampFunction;
|
||||||
@@ -86,6 +87,9 @@ pub static FUNCTION_REGISTRY: Lazy<Arc<FunctionRegistry>> = Lazy::new(|| {
|
|||||||
// Aggregate functions
|
// Aggregate functions
|
||||||
AggregateFunctions::register(&function_registry);
|
AggregateFunctions::register(&function_registry);
|
||||||
|
|
||||||
|
// Full text search function
|
||||||
|
MatchesFunction::register(&function_registry);
|
||||||
|
|
||||||
// System and administration functions
|
// System and administration functions
|
||||||
SystemFunction::register(&function_registry);
|
SystemFunction::register(&function_registry);
|
||||||
TableFunction::register(&function_registry);
|
TableFunction::register(&function_registry);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![feature(try_blocks)]
|
||||||
|
|
||||||
mod macros;
|
mod macros;
|
||||||
pub mod scalars;
|
pub mod scalars;
|
||||||
|
|||||||
@@ -33,10 +33,19 @@ use snafu::{ensure, OptionExt, ResultExt};
|
|||||||
use store_api::storage::ConcreteDataType;
|
use store_api::storage::ConcreteDataType;
|
||||||
|
|
||||||
use crate::function::{Function, FunctionContext};
|
use crate::function::{Function, FunctionContext};
|
||||||
|
use crate::function_registry::FunctionRegistry;
|
||||||
|
|
||||||
/// `matches` for full text search.
|
/// `matches` for full text search.
|
||||||
|
///
|
||||||
|
/// Usage: matches(`<col>`, `<pattern>`) -> boolean
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct MatchesFunction;
|
pub(crate) struct MatchesFunction;
|
||||||
|
|
||||||
|
impl MatchesFunction {
|
||||||
|
pub fn register(registry: &FunctionRegistry) {
|
||||||
|
registry.register(Arc::new(MatchesFunction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for MatchesFunction {
|
impl fmt::Display for MatchesFunction {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@@ -74,12 +83,6 @@ impl Function for MatchesFunction {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
ensure!(
|
|
||||||
columns[1].len() == 1,
|
|
||||||
InvalidFuncArgsSnafu {
|
|
||||||
err_msg: "The second argument should be a string literal",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
let pattern_vector = &columns[1]
|
let pattern_vector = &columns[1]
|
||||||
.cast(&ConcreteDataType::string_datatype())
|
.cast(&ConcreteDataType::string_datatype())
|
||||||
.context(InvalidInputTypeSnafu {
|
.context(InvalidInputTypeSnafu {
|
||||||
@@ -138,14 +141,16 @@ impl MatchesFunction {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
enum PatternAst {
|
enum PatternAst {
|
||||||
Literal {
|
// Distinguish this with `Group` for simplicity
|
||||||
op: UnaryOp,
|
/// A leaf node that matches a column with `pattern`
|
||||||
pattern: String,
|
Literal { op: UnaryOp, pattern: String },
|
||||||
},
|
/// Flattened binary chains
|
||||||
Binary {
|
Binary {
|
||||||
op: BinaryOp,
|
op: BinaryOp,
|
||||||
children: Vec<PatternAst>,
|
children: Vec<PatternAst>,
|
||||||
},
|
},
|
||||||
|
/// A sub-tree enclosed by parenthesis
|
||||||
|
Group { op: UnaryOp, child: Box<PatternAst> },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
@@ -185,6 +190,14 @@ impl PatternAst {
|
|||||||
BinaryOp::Or => exprs.reduce(Expr::or).unwrap(),
|
BinaryOp::Or => exprs.reduce(Expr::or).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PatternAst::Group { op, child } => {
|
||||||
|
let child = child.into_like_expr(column);
|
||||||
|
match op {
|
||||||
|
UnaryOp::Must => child,
|
||||||
|
UnaryOp::Optional => child,
|
||||||
|
UnaryOp::Negative => logical_expr::not(child),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +250,7 @@ impl PatternAst {
|
|||||||
|
|
||||||
for child in children {
|
for child in children {
|
||||||
match child {
|
match child {
|
||||||
PatternAst::Literal { .. } => {
|
PatternAst::Literal { .. } | PatternAst::Group { .. } => {
|
||||||
collapsed.push(child);
|
collapsed.push(child);
|
||||||
}
|
}
|
||||||
PatternAst::Binary { op, children } => {
|
PatternAst::Binary { op, children } => {
|
||||||
@@ -287,10 +300,7 @@ impl PatternAst {
|
|||||||
|
|
||||||
for child in children {
|
for child in children {
|
||||||
match child {
|
match child {
|
||||||
PatternAst::Literal {
|
PatternAst::Literal { op, .. } | PatternAst::Group { op, .. } => match op {
|
||||||
op,
|
|
||||||
pattern: ref _pattern,
|
|
||||||
} => match op {
|
|
||||||
UnaryOp::Must => must_list.push(child),
|
UnaryOp::Must => must_list.push(child),
|
||||||
UnaryOp::Optional => optional_list.push(child),
|
UnaryOp::Optional => optional_list.push(child),
|
||||||
UnaryOp::Negative => must_not_list.push(child),
|
UnaryOp::Negative => must_not_list.push(child),
|
||||||
@@ -336,7 +346,7 @@ impl PatternAst {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eliminate single child node. If a binary node has only one child, it can be
|
/// Eliminate single child [`PatternAst::Binary`] node. If a binary node has only one child, it can be
|
||||||
/// replaced by its only child.
|
/// replaced by its only child.
|
||||||
///
|
///
|
||||||
/// This function prefers to be applied in a top-down manner. But it's not required.
|
/// This function prefers to be applied in a top-down manner. But it's not required.
|
||||||
@@ -351,7 +361,7 @@ impl PatternAst {
|
|||||||
children: grand_children,
|
children: grand_children,
|
||||||
..
|
..
|
||||||
} => !grand_children.is_empty(),
|
} => !grand_children.is_empty(),
|
||||||
PatternAst::Literal { .. } => true,
|
PatternAst::Literal { .. } | PatternAst::Group { .. } => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if children.len() == 1 {
|
if children.len() == 1 {
|
||||||
@@ -375,9 +385,9 @@ impl TreeNode for PatternAst {
|
|||||||
return Ok(TreeNodeRecursion::Stop);
|
return Ok(TreeNodeRecursion::Stop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(TreeNodeRecursion::Continue)
|
Ok(TreeNodeRecursion::Continue)
|
||||||
}
|
}
|
||||||
|
PatternAst::Group { op: _, child } => f(child),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,6 +406,12 @@ impl TreeNode for PatternAst {
|
|||||||
children: new_children,
|
children: new_children,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
PatternAst::Group { op, child } => f(*child)?.map_data(|new_child| {
|
||||||
|
Ok(PatternAst::Group {
|
||||||
|
op,
|
||||||
|
child: Box::new(new_child),
|
||||||
|
})
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,6 +425,7 @@ impl ParserContext {
|
|||||||
pub fn parse_pattern(mut self, pattern: &str) -> Result<PatternAst> {
|
pub fn parse_pattern(mut self, pattern: &str) -> Result<PatternAst> {
|
||||||
let tokenizer = Tokenizer::default();
|
let tokenizer = Tokenizer::default();
|
||||||
let raw_tokens = tokenizer.tokenize(pattern)?;
|
let raw_tokens = tokenizer.tokenize(pattern)?;
|
||||||
|
let raw_tokens = Self::accomplish_optional_unary_op(raw_tokens)?;
|
||||||
let mut tokens = Self::to_rpn(raw_tokens)?;
|
let mut tokens = Self::to_rpn(raw_tokens)?;
|
||||||
|
|
||||||
while !tokens.is_empty() {
|
while !tokens.is_empty() {
|
||||||
@@ -433,6 +450,69 @@ impl ParserContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add [`Token::Optional`] for all bare [`Token::Phase`] and [`Token::Or`]
|
||||||
|
/// for all adjacent [`Token::Phase`]s.
|
||||||
|
///
|
||||||
|
/// This function also does some checks by the way. Like if two unary ops are
|
||||||
|
/// adjacent.
|
||||||
|
fn accomplish_optional_unary_op(raw_tokens: Vec<Token>) -> Result<Vec<Token>> {
|
||||||
|
let mut is_prev_unary_op = false;
|
||||||
|
// The first one doesn't need binary op
|
||||||
|
let mut is_binary_op_before = true;
|
||||||
|
let mut is_unary_op_before = false;
|
||||||
|
let mut new_tokens = Vec::with_capacity(raw_tokens.len());
|
||||||
|
for token in raw_tokens {
|
||||||
|
// fill `Token::Or`
|
||||||
|
if !is_binary_op_before
|
||||||
|
&& matches!(
|
||||||
|
token,
|
||||||
|
Token::Phase(_)
|
||||||
|
| Token::OpenParen
|
||||||
|
| Token::Must
|
||||||
|
| Token::Optional
|
||||||
|
| Token::Negative
|
||||||
|
)
|
||||||
|
{
|
||||||
|
is_binary_op_before = true;
|
||||||
|
new_tokens.push(Token::Or);
|
||||||
|
}
|
||||||
|
if matches!(
|
||||||
|
token,
|
||||||
|
Token::OpenParen // treat open paren as begin of new group
|
||||||
|
| Token::And | Token::Or
|
||||||
|
) {
|
||||||
|
is_binary_op_before = true;
|
||||||
|
} else if matches!(token, Token::Phase(_) | Token::CloseParen) {
|
||||||
|
// need binary op next time
|
||||||
|
is_binary_op_before = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill `Token::Optional`
|
||||||
|
if !is_prev_unary_op && matches!(token, Token::Phase(_) | Token::OpenParen) {
|
||||||
|
new_tokens.push(Token::Optional);
|
||||||
|
} else {
|
||||||
|
is_prev_unary_op = matches!(token, Token::Must | Token::Negative);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if unary ops are adjacent by the way
|
||||||
|
if matches!(token, Token::Must | Token::Optional | Token::Negative) {
|
||||||
|
if is_unary_op_before {
|
||||||
|
return InvalidFuncArgsSnafu {
|
||||||
|
err_msg: "Invalid pattern, unary operators should not be adjacent",
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
is_unary_op_before = true;
|
||||||
|
} else {
|
||||||
|
is_unary_op_before = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_tokens.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(new_tokens)
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert infix token stream to RPN
|
/// Convert infix token stream to RPN
|
||||||
fn to_rpn(mut raw_tokens: Vec<Token>) -> Result<Vec<Token>> {
|
fn to_rpn(mut raw_tokens: Vec<Token>) -> Result<Vec<Token>> {
|
||||||
let mut operator_stack = vec![];
|
let mut operator_stack = vec![];
|
||||||
@@ -442,44 +522,51 @@ impl ParserContext {
|
|||||||
while let Some(token) = raw_tokens.pop() {
|
while let Some(token) = raw_tokens.pop() {
|
||||||
match token {
|
match token {
|
||||||
Token::Phase(_) => result.push(token),
|
Token::Phase(_) => result.push(token),
|
||||||
Token::Must | Token::Negative => {
|
Token::Must | Token::Negative | Token::Optional => {
|
||||||
// unary operator with paren is not handled yet
|
operator_stack.push(token);
|
||||||
let phase = raw_tokens.pop().context(InvalidFuncArgsSnafu {
|
|
||||||
err_msg: "Unexpected end of pattern, expected a phase after unary operator",
|
|
||||||
})?;
|
|
||||||
result.push(phase);
|
|
||||||
result.push(token);
|
|
||||||
}
|
}
|
||||||
Token::OpenParen => operator_stack.push(token),
|
Token::OpenParen => operator_stack.push(token),
|
||||||
Token::And | Token::Or => {
|
Token::And | Token::Or => {
|
||||||
// Or has lower priority than And
|
// - Or has lower priority than And
|
||||||
|
// - Binary op have lower priority than unary op
|
||||||
while let Some(stack_top) = operator_stack.last()
|
while let Some(stack_top) = operator_stack.last()
|
||||||
&& *stack_top == Token::And
|
&& ((*stack_top == Token::And && token == Token::Or)
|
||||||
&& token == Token::Or
|
|| matches!(
|
||||||
|
*stack_top,
|
||||||
|
Token::Must | Token::Optional | Token::Negative
|
||||||
|
))
|
||||||
{
|
{
|
||||||
result.push(operator_stack.pop().unwrap());
|
result.push(operator_stack.pop().unwrap());
|
||||||
}
|
}
|
||||||
operator_stack.push(token);
|
operator_stack.push(token);
|
||||||
}
|
}
|
||||||
Token::CloseParen => {
|
Token::CloseParen => {
|
||||||
|
let mut is_open_paren_found = false;
|
||||||
while let Some(op) = operator_stack.pop() {
|
while let Some(op) = operator_stack.pop() {
|
||||||
if op == Token::OpenParen {
|
if op == Token::OpenParen {
|
||||||
|
is_open_paren_found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
result.push(op);
|
result.push(op);
|
||||||
}
|
}
|
||||||
|
if !is_open_paren_found {
|
||||||
|
return InvalidFuncArgsSnafu {
|
||||||
|
err_msg: "Unmatched close parentheses",
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(operand) = operator_stack.pop() {
|
while let Some(operator) = operator_stack.pop() {
|
||||||
if operand == Token::OpenParen {
|
if operator == Token::OpenParen {
|
||||||
return InvalidFuncArgsSnafu {
|
return InvalidFuncArgsSnafu {
|
||||||
err_msg: "Unmatched parentheses",
|
err_msg: "Unmatched parentheses",
|
||||||
}
|
}
|
||||||
.fail();
|
.fail();
|
||||||
}
|
}
|
||||||
result.push(operand);
|
result.push(operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@@ -489,36 +576,85 @@ impl ParserContext {
|
|||||||
if let Some(token) = tokens.pop() {
|
if let Some(token) = tokens.pop() {
|
||||||
match token {
|
match token {
|
||||||
Token::Must => {
|
Token::Must => {
|
||||||
let phase = tokens.pop().context(InvalidFuncArgsSnafu {
|
if self.stack.is_empty() {
|
||||||
err_msg: "Unexpected end of pattern, expected a phase after \"+\" operator",
|
self.parse_one_impl(tokens)?;
|
||||||
|
}
|
||||||
|
let phase_or_group = self.stack.pop().context(InvalidFuncArgsSnafu {
|
||||||
|
err_msg: "Invalid pattern, \"+\" operator should have one operand",
|
||||||
})?;
|
})?;
|
||||||
self.stack.push(PatternAst::Literal {
|
match phase_or_group {
|
||||||
op: UnaryOp::Must,
|
PatternAst::Literal { op: _, pattern } => {
|
||||||
pattern: Self::unwrap_phase(phase)?,
|
self.stack.push(PatternAst::Literal {
|
||||||
});
|
op: UnaryOp::Must,
|
||||||
|
pattern,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
PatternAst::Binary { .. } | PatternAst::Group { .. } => {
|
||||||
|
self.stack.push(PatternAst::Group {
|
||||||
|
op: UnaryOp::Must,
|
||||||
|
child: Box::new(phase_or_group),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Token::Negative => {
|
Token::Negative => {
|
||||||
let phase = tokens.pop().context(InvalidFuncArgsSnafu {
|
if self.stack.is_empty() {
|
||||||
err_msg: "Unexpected end of pattern, expected a phase after \"-\" operator",
|
self.parse_one_impl(tokens)?;
|
||||||
|
}
|
||||||
|
let phase_or_group = self.stack.pop().context(InvalidFuncArgsSnafu {
|
||||||
|
err_msg: "Invalid pattern, \"-\" operator should have one operand",
|
||||||
})?;
|
})?;
|
||||||
self.stack.push(PatternAst::Literal {
|
match phase_or_group {
|
||||||
op: UnaryOp::Negative,
|
PatternAst::Literal { op: _, pattern } => {
|
||||||
pattern: Self::unwrap_phase(phase)?,
|
self.stack.push(PatternAst::Literal {
|
||||||
});
|
op: UnaryOp::Negative,
|
||||||
|
pattern,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
PatternAst::Binary { .. } | PatternAst::Group { .. } => {
|
||||||
|
self.stack.push(PatternAst::Group {
|
||||||
|
op: UnaryOp::Negative,
|
||||||
|
child: Box::new(phase_or_group),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Token::Phase(token) => {
|
Token::Optional => {
|
||||||
let phase = token.clone();
|
if self.stack.is_empty() {
|
||||||
self.stack.push(PatternAst::Literal {
|
self.parse_one_impl(tokens)?;
|
||||||
op: UnaryOp::Optional,
|
}
|
||||||
pattern: phase,
|
let phase_or_group = self.stack.pop().context(InvalidFuncArgsSnafu {
|
||||||
});
|
err_msg:
|
||||||
|
"Invalid pattern, OPTIONAL(space) operator should have one operand",
|
||||||
|
})?;
|
||||||
|
match phase_or_group {
|
||||||
|
PatternAst::Literal { op: _, pattern } => {
|
||||||
|
self.stack.push(PatternAst::Literal {
|
||||||
|
op: UnaryOp::Optional,
|
||||||
|
pattern,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
PatternAst::Binary { .. } | PatternAst::Group { .. } => {
|
||||||
|
self.stack.push(PatternAst::Group {
|
||||||
|
op: UnaryOp::Optional,
|
||||||
|
child: Box::new(phase_or_group),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
Token::Phase(pattern) => {
|
||||||
|
self.stack.push(PatternAst::Literal {
|
||||||
|
// Op here is a placeholder
|
||||||
|
op: UnaryOp::Optional,
|
||||||
|
pattern,
|
||||||
|
})
|
||||||
|
}
|
||||||
Token::And => {
|
Token::And => {
|
||||||
if self.stack.is_empty() {
|
if self.stack.is_empty() {
|
||||||
self.parse_one_impl(tokens)?
|
self.parse_one_impl(tokens)?;
|
||||||
};
|
};
|
||||||
let rhs = self.stack.pop().context(InvalidFuncArgsSnafu {
|
let rhs = self.stack.pop().context(InvalidFuncArgsSnafu {
|
||||||
err_msg: "Invalid pattern, \"AND\" operator should have two operands",
|
err_msg: "Invalid pattern, \"AND\" operator should have two operands",
|
||||||
@@ -565,16 +701,6 @@ impl ParserContext {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_phase(token: Token) -> Result<String> {
|
|
||||||
match token {
|
|
||||||
Token::Phase(phase) => Ok(phase),
|
|
||||||
_ => InvalidFuncArgsSnafu {
|
|
||||||
err_msg: format!("Unexpected token: {:?}, want a phase", token),
|
|
||||||
}
|
|
||||||
.fail(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@@ -593,6 +719,12 @@ enum Token {
|
|||||||
CloseParen,
|
CloseParen,
|
||||||
/// Any other phases
|
/// Any other phases
|
||||||
Phase(String),
|
Phase(String),
|
||||||
|
|
||||||
|
/// This is not a token from user input, but a placeholder for internal use.
|
||||||
|
/// It's used to accomplish the unary operator class with Must and Negative.
|
||||||
|
/// In user provided pattern, optional is expressed by a bare phase or group
|
||||||
|
/// (simply nothing or writespace).
|
||||||
|
Optional,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -862,11 +994,7 @@ mod test {
|
|||||||
children: vec![
|
children: vec![
|
||||||
PatternAst::Literal {
|
PatternAst::Literal {
|
||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
pattern: "d".to_string(),
|
pattern: "a".to_string(),
|
||||||
},
|
|
||||||
PatternAst::Literal {
|
|
||||||
op: UnaryOp::Optional,
|
|
||||||
pattern: "c".to_string(),
|
|
||||||
},
|
},
|
||||||
PatternAst::Literal {
|
PatternAst::Literal {
|
||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
@@ -874,7 +1002,11 @@ mod test {
|
|||||||
},
|
},
|
||||||
PatternAst::Literal {
|
PatternAst::Literal {
|
||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
pattern: "a".to_string(),
|
pattern: "c".to_string(),
|
||||||
|
},
|
||||||
|
PatternAst::Literal {
|
||||||
|
op: UnaryOp::Optional,
|
||||||
|
pattern: "d".to_string(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -886,11 +1018,11 @@ mod test {
|
|||||||
children: vec![
|
children: vec![
|
||||||
PatternAst::Literal {
|
PatternAst::Literal {
|
||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
pattern: "b".to_string(),
|
pattern: "a".to_string(),
|
||||||
},
|
},
|
||||||
PatternAst::Literal {
|
PatternAst::Literal {
|
||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
pattern: "a".to_string(),
|
pattern: "b".to_string(),
|
||||||
},
|
},
|
||||||
PatternAst::Binary {
|
PatternAst::Binary {
|
||||||
op: BinaryOp::And,
|
op: BinaryOp::And,
|
||||||
@@ -918,6 +1050,27 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_ast() {
|
||||||
|
let cases = [
|
||||||
|
(r#"a b (c"#, "Unmatched parentheses"),
|
||||||
|
(r#"a b) c"#, "Unmatched close parentheses"),
|
||||||
|
(r#"a +-b"#, "unary operators should not be adjacent"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (query, expected) in cases {
|
||||||
|
let result: Result<()> = try {
|
||||||
|
let parser = ParserContext { stack: vec![] };
|
||||||
|
let ast = parser.parse_pattern(query)?;
|
||||||
|
let _ast = ast.transform_ast()?;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(result.is_err(), "{query}");
|
||||||
|
let actual_error = result.unwrap_err().to_string();
|
||||||
|
assert!(actual_error.contains(expected), "{query}, {actual_error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn valid_matches_parser() {
|
fn valid_matches_parser() {
|
||||||
let cases = [
|
let cases = [
|
||||||
@@ -951,18 +1104,21 @@ mod test {
|
|||||||
PatternAst::Binary {
|
PatternAst::Binary {
|
||||||
op: BinaryOp::Or,
|
op: BinaryOp::Or,
|
||||||
children: vec![
|
children: vec![
|
||||||
PatternAst::Binary {
|
PatternAst::Group {
|
||||||
op: BinaryOp::And,
|
op: UnaryOp::Optional,
|
||||||
children: vec![
|
child: Box::new(PatternAst::Binary {
|
||||||
PatternAst::Literal {
|
op: BinaryOp::And,
|
||||||
op: UnaryOp::Optional,
|
children: vec![
|
||||||
pattern: "a".to_string(),
|
PatternAst::Literal {
|
||||||
},
|
op: UnaryOp::Optional,
|
||||||
PatternAst::Literal {
|
pattern: "a".to_string(),
|
||||||
op: UnaryOp::Optional,
|
},
|
||||||
pattern: "b".to_string(),
|
PatternAst::Literal {
|
||||||
},
|
op: UnaryOp::Optional,
|
||||||
],
|
pattern: "b".to_string(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
PatternAst::Literal {
|
PatternAst::Literal {
|
||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
@@ -980,18 +1136,21 @@ mod test {
|
|||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
pattern: "a".to_string(),
|
pattern: "a".to_string(),
|
||||||
},
|
},
|
||||||
PatternAst::Binary {
|
PatternAst::Group {
|
||||||
op: BinaryOp::Or,
|
op: UnaryOp::Optional,
|
||||||
children: vec![
|
child: Box::new(PatternAst::Binary {
|
||||||
PatternAst::Literal {
|
op: BinaryOp::Or,
|
||||||
op: UnaryOp::Optional,
|
children: vec![
|
||||||
pattern: "b".to_string(),
|
PatternAst::Literal {
|
||||||
},
|
op: UnaryOp::Optional,
|
||||||
PatternAst::Literal {
|
pattern: "b".to_string(),
|
||||||
op: UnaryOp::Optional,
|
},
|
||||||
pattern: "c".to_string(),
|
PatternAst::Literal {
|
||||||
},
|
op: UnaryOp::Optional,
|
||||||
],
|
pattern: "c".to_string(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -1001,18 +1160,23 @@ mod test {
|
|||||||
PatternAst::Binary {
|
PatternAst::Binary {
|
||||||
op: BinaryOp::Or,
|
op: BinaryOp::Or,
|
||||||
children: vec![
|
children: vec![
|
||||||
PatternAst::Literal {
|
|
||||||
op: UnaryOp::Negative,
|
|
||||||
pattern: "c".to_string(),
|
|
||||||
},
|
|
||||||
PatternAst::Literal {
|
|
||||||
op: UnaryOp::Must,
|
|
||||||
pattern: "b".to_string(),
|
|
||||||
},
|
|
||||||
PatternAst::Literal {
|
PatternAst::Literal {
|
||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
pattern: "a".to_string(),
|
pattern: "a".to_string(),
|
||||||
},
|
},
|
||||||
|
PatternAst::Binary {
|
||||||
|
op: BinaryOp::Or,
|
||||||
|
children: vec![
|
||||||
|
PatternAst::Literal {
|
||||||
|
op: UnaryOp::Must,
|
||||||
|
pattern: "b".to_string(),
|
||||||
|
},
|
||||||
|
PatternAst::Literal {
|
||||||
|
op: UnaryOp::Negative,
|
||||||
|
pattern: "c".to_string(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -1021,18 +1185,26 @@ mod test {
|
|||||||
PatternAst::Binary {
|
PatternAst::Binary {
|
||||||
op: BinaryOp::Or,
|
op: BinaryOp::Or,
|
||||||
children: vec![
|
children: vec![
|
||||||
|
PatternAst::Group {
|
||||||
|
op: UnaryOp::Optional,
|
||||||
|
child: Box::new(PatternAst::Binary {
|
||||||
|
op: BinaryOp::Or,
|
||||||
|
children: vec![
|
||||||
|
PatternAst::Literal {
|
||||||
|
op: UnaryOp::Must,
|
||||||
|
pattern: "a".to_string(),
|
||||||
|
},
|
||||||
|
PatternAst::Literal {
|
||||||
|
op: UnaryOp::Must,
|
||||||
|
pattern: "b".to_string(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
PatternAst::Literal {
|
PatternAst::Literal {
|
||||||
op: UnaryOp::Optional,
|
op: UnaryOp::Optional,
|
||||||
pattern: "c".to_string(),
|
pattern: "c".to_string(),
|
||||||
},
|
},
|
||||||
PatternAst::Literal {
|
|
||||||
op: UnaryOp::Must,
|
|
||||||
pattern: "b".to_string(),
|
|
||||||
},
|
|
||||||
PatternAst::Literal {
|
|
||||||
op: UnaryOp::Must,
|
|
||||||
pattern: "a".to_string(),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -1062,7 +1234,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evaluate_matches_without_parenthesis() {
|
fn evaluate_matches() {
|
||||||
let input_data = vec![
|
let input_data = vec![
|
||||||
"The quick brown fox jumps over the lazy dog",
|
"The quick brown fox jumps over the lazy dog",
|
||||||
"The fox jumps over the lazy dog",
|
"The fox jumps over the lazy dog",
|
||||||
@@ -1100,7 +1272,7 @@ mod test {
|
|||||||
"-over AND -lazy",
|
"-over AND -lazy",
|
||||||
vec![false, false, false, false, false, false, false],
|
vec![false, false, false, false, false, false, false],
|
||||||
),
|
),
|
||||||
// test priority between AND & OR
|
// priority between AND & OR
|
||||||
(
|
(
|
||||||
"fox AND jumps OR over",
|
"fox AND jumps OR over",
|
||||||
vec![true, true, true, true, true, true, true],
|
vec![true, true, true, true, true, true, true],
|
||||||
@@ -1138,11 +1310,27 @@ mod test {
|
|||||||
"fox AND +jumps AND -over",
|
"fox AND +jumps AND -over",
|
||||||
vec![false, false, false, false, true, false, false],
|
vec![false, false, false, false, true, false, false],
|
||||||
),
|
),
|
||||||
// TODO: still parentheses are not supported
|
// weird parentheses cases
|
||||||
// (
|
(
|
||||||
// "(+fox +jumps) over",
|
"(+fox +jumps) over",
|
||||||
// vec![true, true, true, true, true, true, true],
|
vec![true, true, true, true, true, true, true],
|
||||||
// ),
|
),
|
||||||
|
(
|
||||||
|
"+(fox jumps) AND over",
|
||||||
|
vec![true, true, true, true, false, true, true],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"over -(fox jumps)",
|
||||||
|
vec![false, false, false, false, false, false, false],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"over -(fox AND jumps)",
|
||||||
|
vec![false, false, true, true, false, false, false],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"over AND -(-(fox OR jumps))",
|
||||||
|
vec![true, true, true, true, false, true, true],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
let f = MatchesFunction;
|
let f = MatchesFunction;
|
||||||
|
|||||||
259
tests/cases/standalone/common/select/matches.result
Normal file
259
tests/cases/standalone/common/select/matches.result
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
create table fox (
|
||||||
|
ts timestamp time index,
|
||||||
|
fox string,
|
||||||
|
);
|
||||||
|
|
||||||
|
Affected Rows: 0
|
||||||
|
|
||||||
|
insert into fox values
|
||||||
|
(1, 'The quick brown fox jumps over the lazy dog'),
|
||||||
|
(2, 'The fox jumps over the lazy dog'),
|
||||||
|
(3, 'The quick brown jumps over the lazy dog'),
|
||||||
|
(4, 'The quick brown fox over the lazy dog'),
|
||||||
|
(5, 'The quick brown fox jumps the lazy dog'),
|
||||||
|
(6, 'The quick brown fox jumps over dog'),
|
||||||
|
(7, 'The quick brown fox jumps over the dog');
|
||||||
|
|
||||||
|
Affected Rows: 7
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '"fox jumps"') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '"quick brown"') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '"fox jumps"') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox OR lazy') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox AND lazy') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '-over -lazy') order by ts;
|
||||||
|
|
||||||
|
++
|
||||||
|
++
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '-over AND -lazy') order by ts;
|
||||||
|
|
||||||
|
++
|
||||||
|
++
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox AND jumps OR over') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox OR brown AND quick') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '(fox OR brown) AND quick') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'brown AND quick OR fox') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'brown AND (quick OR fox)') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'brown AND quick AND fox OR jumps AND over AND lazy') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'quick brown fox +jumps') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox +jumps -over') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox AND +jumps AND -over') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '(+fox +jumps) over') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '+(fox jumps) AND over') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'over -(fox jumps)') order by ts;
|
||||||
|
|
||||||
|
++
|
||||||
|
++
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'over -(fox AND jumps)') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'over AND -(-(fox OR jumps))') order by ts;
|
||||||
|
|
||||||
|
+---------------------------------------------+
|
||||||
|
| fox |
|
||||||
|
+---------------------------------------------+
|
||||||
|
| The quick brown fox jumps over the lazy dog |
|
||||||
|
| The fox jumps over the lazy dog |
|
||||||
|
| The quick brown jumps over the lazy dog |
|
||||||
|
| The quick brown fox over the lazy dog |
|
||||||
|
| The quick brown fox jumps over dog |
|
||||||
|
| The quick brown fox jumps over the dog |
|
||||||
|
+---------------------------------------------+
|
||||||
|
|
||||||
|
drop table fox;
|
||||||
|
|
||||||
|
Affected Rows: 0
|
||||||
|
|
||||||
57
tests/cases/standalone/common/select/matches.sql
Normal file
57
tests/cases/standalone/common/select/matches.sql
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
create table fox (
|
||||||
|
ts timestamp time index,
|
||||||
|
fox string,
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into fox values
|
||||||
|
(1, 'The quick brown fox jumps over the lazy dog'),
|
||||||
|
(2, 'The fox jumps over the lazy dog'),
|
||||||
|
(3, 'The quick brown jumps over the lazy dog'),
|
||||||
|
(4, 'The quick brown fox over the lazy dog'),
|
||||||
|
(5, 'The quick brown fox jumps the lazy dog'),
|
||||||
|
(6, 'The quick brown fox jumps over dog'),
|
||||||
|
(7, 'The quick brown fox jumps over the dog');
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '"fox jumps"') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '"quick brown"') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '"fox jumps"') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox OR lazy') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox AND lazy') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '-over -lazy') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '-over AND -lazy') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox AND jumps OR over') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox OR brown AND quick') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '(fox OR brown) AND quick') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'brown AND quick OR fox') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'brown AND (quick OR fox)') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'brown AND quick AND fox OR jumps AND over AND lazy') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'quick brown fox +jumps') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox +jumps -over') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'fox AND +jumps AND -over') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '(+fox +jumps) over') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, '+(fox jumps) AND over') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'over -(fox jumps)') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'over -(fox AND jumps)') order by ts;
|
||||||
|
|
||||||
|
select fox from fox where matches(fox, 'over AND -(-(fox OR jumps))') order by ts;
|
||||||
|
|
||||||
|
drop table fox;
|
||||||
Reference in New Issue
Block a user