support regexp search in single field

This commit is contained in:
Damien Elmes 2020-03-21 13:06:46 +10:00
parent 97577dbc16
commit f0ed34d79b
2 changed files with 41 additions and 10 deletions

View file

@ -53,6 +53,7 @@ pub(super) enum SearchNode<'a> {
SingleField { SingleField {
field: Cow<'a, str>, field: Cow<'a, str>,
text: Cow<'a, str>, text: Cow<'a, str>,
is_re: bool,
}, },
AddedInDays(u32), AddedInDays(u32),
CardTemplate(TemplateKind), CardTemplate(TemplateKind),
@ -272,10 +273,7 @@ fn search_node_for_text_with_argument<'a>(
"prop" => parse_prop(val.as_ref())?, "prop" => parse_prop(val.as_ref())?,
"re" => SearchNode::Regex(val), "re" => SearchNode::Regex(val),
// anything else is a field search // anything else is a field search
_ => SearchNode::SingleField { _ => parse_single_field(key.as_ref(), val.as_ref()),
field: key,
text: val,
},
}) })
} }
@ -392,6 +390,20 @@ fn parse_template(val: &str) -> SearchNode<'static> {
}) })
} }
fn parse_single_field(key: &str, mut val: &str) -> SearchNode<'static> {
let is_re = if val.starts_with("re:") {
val = val.trim_start_matches("re:");
true
} else {
false
};
SearchNode::SingleField {
field: key.to_string().into(),
text: val.to_string().into(),
is_re,
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -424,7 +436,8 @@ mod test {
And, And,
Search(SingleField { Search(SingleField {
field: "foo".into(), field: "foo".into(),
text: "bar baz".into() text: "bar baz".into(),
is_re: false,
}) })
]))), ]))),
Or, Or,
@ -432,6 +445,15 @@ mod test {
] ]
); );
assert_eq!(
parse("foo:re:bar")?,
vec![Search(SingleField {
field: "foo".into(),
text: "bar".into(),
is_re: true
})]
);
// any character should be escapable in quotes // any character should be escapable in quotes
assert_eq!( assert_eq!(
parse(r#""re:\btest""#)?, parse(r#""re:\btest""#)?,

View file

@ -55,8 +55,8 @@ impl SqlWriter<'_, '_> {
fn write_search_node_to_sql(&mut self, node: &SearchNode) -> Result<()> { fn write_search_node_to_sql(&mut self, node: &SearchNode) -> Result<()> {
match node { match node {
SearchNode::UnqualifiedText(text) => self.write_unqualified(text), SearchNode::UnqualifiedText(text) => self.write_unqualified(text),
SearchNode::SingleField { field, text } => { SearchNode::SingleField { field, text, is_re } => {
self.write_single_field(field.as_ref(), text.as_ref())? self.write_single_field(field.as_ref(), text.as_ref(), *is_re)?
} }
SearchNode::AddedInDays(days) => self.write_added(*days)?, SearchNode::AddedInDays(days) => self.write_added(*days)?,
SearchNode::CardTemplate(template) => self.write_template(template)?, SearchNode::CardTemplate(template) => self.write_template(template)?,
@ -279,7 +279,7 @@ impl SqlWriter<'_, '_> {
Ok(()) Ok(())
} }
fn write_single_field(&mut self, field_name: &str, val: &str) -> Result<()> { fn write_single_field(&mut self, field_name: &str, val: &str, is_re: bool) -> Result<()> {
let note_types = self.req.storage.all_note_types()?; let note_types = self.req.storage.all_note_types()?;
let mut field_map = vec![]; let mut field_map = vec![];
@ -299,15 +299,24 @@ impl SqlWriter<'_, '_> {
return Ok(()); return Ok(());
} }
self.args.push(val.replace('*', "%")); let cmp;
if is_re {
cmp = "regexp";
self.args.push(format!("(?i){}", val));
} else {
cmp = "like";
self.args.push(val.replace('*', "%"));
}
let arg_idx = self.args.len(); let arg_idx = self.args.len();
let searches: Vec<_> = field_map let searches: Vec<_> = field_map
.iter() .iter()
.map(|(ntid, ord)| { .map(|(ntid, ord)| {
format!( format!(
"(n.mid = {mid} and field_at_index(n.flds, {ord}) like ?{n})", "(n.mid = {mid} and field_at_index(n.flds, {ord}) {cmp} ?{n})",
mid = ntid, mid = ntid,
ord = ord, ord = ord,
cmp = cmp,
n = arg_idx n = arg_idx
) )
}) })