fix(search): pass FTS5 boolean operators through unquoted

FTS5 boolean operators (AND, OR, NOT, NEAR) are case-sensitive uppercase
keywords that must appear unquoted in the query string. Previously, the
user-friendly query builder would double-quote every token, causing
queries like "switch AND health" to search for the literal word "AND"
instead of using it as a boolean conjunction.

Adds a FTS5_OPERATORS constant and checks each token against it before
quoting, allowing natural boolean search syntax to work as expected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
teernisse
2026-02-13 14:56:19 -05:00
parent f36e900570
commit 59f65b127a

View File

@@ -52,12 +52,18 @@ pub fn to_fts_query(raw: &str, mode: FtsQueryMode) -> String {
return String::new(); return String::new();
} }
// FTS5 boolean operators are case-sensitive uppercase keywords.
// Pass them through unquoted so users can write "switch AND health".
const FTS5_OPERATORS: &[&str] = &["AND", "OR", "NOT", "NEAR"];
let mut result = String::with_capacity(trimmed.len() + 20); let mut result = String::with_capacity(trimmed.len() + 20);
for (i, token) in trimmed.split_whitespace().enumerate() { for (i, token) in trimmed.split_whitespace().enumerate() {
if i > 0 { if i > 0 {
result.push(' '); result.push(' ');
} }
if let Some(stem) = token.strip_suffix('*') if FTS5_OPERATORS.contains(&token) {
result.push_str(token);
} else if let Some(stem) = token.strip_suffix('*')
&& !stem.is_empty() && !stem.is_empty()
&& stem.chars().all(|c| c.is_alphanumeric() || c == '_') && stem.chars().all(|c| c.is_alphanumeric() || c == '_')
{ {