fix(quick_search): resolve word issues to not trigger substring matches (#7708)

This commit is contained in:
Elian Doran 2025-11-13 09:53:14 +02:00 committed by GitHub
commit 4f580a37a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -113,7 +113,16 @@ class NoteContentFulltextExp extends Expression {
const normalizedFlatText = normalizeSearchText(flatText);
// Check if =phrase appears in flatText (indicates attribute value match)
matches = normalizedFlatText.includes(`=${normalizedPhrase}`);
// For single words, use word-boundary matching to avoid substring matches
if (!normalizedPhrase.includes(' ')) {
// Single word: look for =word with word boundaries
// Split by = to get attribute values, then check each value for exact word match
const parts = normalizedFlatText.split('=');
matches = parts.slice(1).some(part => this.exactWordMatch(normalizedPhrase, part));
} else {
// Multi-word phrase: check for substring match
matches = normalizedFlatText.includes(`=${normalizedPhrase}`);
}
if ((this.operator === "=" && matches) || (this.operator === "!=" && !matches)) {
resultNoteSet.add(noteFromBecca);
@ -124,6 +133,17 @@ class NoteContentFulltextExp extends Expression {
return resultNoteSet;
}
/**
* Helper method to check if a single word appears as an exact match in text
* @param wordToFind - The word to search for (should be normalized)
* @param text - The text to search in (should be normalized)
* @returns true if the word is found as an exact match (not substring)
*/
private exactWordMatch(wordToFind: string, text: string): boolean {
const words = text.split(/\s+/);
return words.some(word => word === wordToFind);
}
/**
* Checks if content contains the exact word (with word boundaries) or exact phrase
* This is case-insensitive since content and token are already normalized
@ -139,9 +159,8 @@ class NoteContentFulltextExp extends Expression {
return normalizedContent.includes(normalizedToken);
}
// For single words, split content into words and check for exact match
const words = normalizedContent.split(/\s+/);
return words.some(word => word === normalizedToken);
// For single words, use exact word matching to avoid substring matches
return this.exactWordMatch(normalizedToken, normalizedContent);
}
/**
@ -155,7 +174,14 @@ class NoteContentFulltextExp extends Expression {
// Join tokens with single space to form the phrase
const phrase = normalizedTokens.join(" ");
// Check if the phrase appears as a substring (consecutive words)
// For single-word phrases, use word-boundary matching to avoid substring matches
// e.g., "asd" should not match "asdfasdf"
if (!phrase.includes(' ')) {
// Single word: use exact word matching to avoid substring matches
return this.exactWordMatch(phrase, normalizedContent);
}
// For multi-word phrases, check if the phrase appears as consecutive words
if (normalizedContent.includes(phrase)) {
return true;
}