mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 23:42:23 -04:00
support regexp search in single field
This commit is contained in:
parent
97577dbc16
commit
f0ed34d79b
2 changed files with 41 additions and 10 deletions
|
@ -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""#)?,
|
||||||
|
|
|
@ -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(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cmp;
|
||||||
|
if is_re {
|
||||||
|
cmp = "regexp";
|
||||||
|
self.args.push(format!("(?i){}", val));
|
||||||
|
} else {
|
||||||
|
cmp = "like";
|
||||||
self.args.push(val.replace('*', "%"));
|
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
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue