Always use regex for tag search
Don't distinguish between the glob and no-glob cases when comparing tags but always use regexp. Thus, avoid problems with SQL wildcards in registered tags.
This commit is contained in:
parent
d1ee507b3a
commit
57787368a1
@ -76,7 +76,7 @@ pub(super) enum SearchNode<'a> {
|
|||||||
days: u32,
|
days: u32,
|
||||||
ease: Option<u8>,
|
ease: Option<u8>,
|
||||||
},
|
},
|
||||||
Tag(OptionalRe<'a>),
|
Tag(String),
|
||||||
Duplicates {
|
Duplicates {
|
||||||
note_type_id: NoteTypeID,
|
note_type_id: NoteTypeID,
|
||||||
text: Cow<'a, str>,
|
text: Cow<'a, str>,
|
||||||
@ -294,18 +294,18 @@ fn search_node_for_text_with_argument<'a>(
|
|||||||
"prop" => parse_prop(val)?,
|
"prop" => parse_prop(val)?,
|
||||||
"re" => SearchNode::Regex(unescape_quotes(val)),
|
"re" => SearchNode::Regex(unescape_quotes(val)),
|
||||||
"nc" => SearchNode::NoCombining(unescape_to_glob(val)?),
|
"nc" => SearchNode::NoCombining(unescape_to_glob(val)?),
|
||||||
"w" => SearchNode::WordBoundary(unescape_to_enforced_re(val)?),
|
"w" => SearchNode::WordBoundary(unescape_to_enforced_re(val, ".")?),
|
||||||
// anything else is a field search
|
// anything else is a field search
|
||||||
_ => parse_single_field(key, val)?,
|
_ => parse_single_field(key, val)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure the string doesn't contain whitespace and unescape.
|
/// Ensure the string doesn't contain whitespace and unescape.
|
||||||
fn parse_tag(s: &str) -> ParseResult<OptionalRe> {
|
fn parse_tag(s: &str) -> ParseResult<String> {
|
||||||
if s.as_bytes().iter().any(u8::is_ascii_whitespace) {
|
if s.as_bytes().iter().any(u8::is_ascii_whitespace) {
|
||||||
Err(ParseError {})
|
Err(ParseError {})
|
||||||
} else {
|
} else {
|
||||||
unescape_to_custom_re(s, r"\S")
|
unescape_to_enforced_re(s, r"\S")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,8 +527,8 @@ fn unescape_to_custom_re<'a>(txt: &'a str, wildcard: &str) -> ParseResult<Option
|
|||||||
|
|
||||||
/// Handle escaped characters and convert to regex.
|
/// Handle escaped characters and convert to regex.
|
||||||
/// Return error if there is an undefined escape sequence.
|
/// Return error if there is an undefined escape sequence.
|
||||||
fn unescape_to_enforced_re(txt: &str) -> ParseResult<String> {
|
fn unescape_to_enforced_re(txt: &str, wildcard: &str) -> ParseResult<String> {
|
||||||
Ok(match unescape_to_re(txt)? {
|
Ok(match unescape_to_custom_re(txt, wildcard)? {
|
||||||
OptionalRe::Text(s) => regex::escape(s.as_ref()),
|
OptionalRe::Text(s) => regex::escape(s.as_ref()),
|
||||||
OptionalRe::Re(s) => s.to_string(),
|
OptionalRe::Re(s) => s.to_string(),
|
||||||
})
|
})
|
||||||
@ -643,7 +643,7 @@ mod test {
|
|||||||
parse("note:basic")?,
|
parse("note:basic")?,
|
||||||
vec![Search(NoteType(Text("basic".into())))]
|
vec![Search(NoteType(Text("basic".into())))]
|
||||||
);
|
);
|
||||||
assert_eq!(parse("tag:hard")?, vec![Search(Tag(Text("hard".into())))]);
|
assert_eq!(parse("tag:hard")?, vec![Search(Tag("hard".to_string()))]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("nid:1237123712,2,3")?,
|
parse("nid:1237123712,2,3")?,
|
||||||
vec![Search(NoteIDs("1237123712,2,3".into()))]
|
vec![Search(NoteIDs("1237123712,2,3".into()))]
|
||||||
|
@ -188,28 +188,16 @@ impl SqlWriter<'_> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_tag(&mut self, text: &OptionalRe) -> Result<()> {
|
fn write_tag(&mut self, s: &String) -> Result<()> {
|
||||||
match text {
|
match s.as_str() {
|
||||||
OptionalRe::Text(s) => {
|
"none" => write!(self.sql, "n.tags = ''").unwrap(),
|
||||||
if s == "none" {
|
r"\S*" => write!(self.sql, "true").unwrap(),
|
||||||
write!(self.sql, "n.tags = ''").unwrap();
|
_ => {
|
||||||
} else if let Some(tag) = self.col.storage.preferred_tag_case(s)? {
|
|
||||||
write!(self.sql, "n.tags like ?").unwrap();
|
|
||||||
self.args.push(format!("% {} %", tag));
|
|
||||||
} else {
|
|
||||||
write!(self.sql, "false").unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OptionalRe::Re(s) => {
|
|
||||||
if s == r"\S*" {
|
|
||||||
write!(self.sql, "true").unwrap();
|
|
||||||
} else {
|
|
||||||
let re = format!("(?i).* {} .*", s);
|
|
||||||
write!(self.sql, "n.tags regexp ?").unwrap();
|
write!(self.sql, "n.tags regexp ?").unwrap();
|
||||||
self.args.push(re);
|
self.args.push(format!("(?i).* {} .*", s));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -668,15 +656,12 @@ mod test {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// unregistered tag short circuits
|
|
||||||
assert_eq!(s(ctx, r"tag:one"), ("(false)".into(), vec![]));
|
|
||||||
|
|
||||||
// if registered, searches with canonical
|
// if registered, searches with canonical
|
||||||
ctx.transact(None, |col| col.register_tag("One", Usn(-1)))
|
ctx.transact(None, |col| col.register_tag("One", Usn(-1)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
s(ctx, r"tag:one"),
|
s(ctx, r"tag:one"),
|
||||||
("(n.tags like ?)".into(), vec![r"% One %".into()])
|
("(n.tags regexp ?)".into(), vec![r"(?i).* one .*".into()])
|
||||||
);
|
);
|
||||||
|
|
||||||
// wildcards force a regexp search
|
// wildcards force a regexp search
|
||||||
|
Loading…
Reference in New Issue
Block a user