Skip to content

Commit 4f580a3

Browse files
authored
fix(quick_search): resolve word issues to not trigger substring matches (#7708)
2 parents f64d11b + e89646e commit 4f580a3

File tree

1 file changed

+31
-5
lines changed

1 file changed

+31
-5
lines changed

apps/server/src/services/search/expressions/note_content_fulltext.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,16 @@ class NoteContentFulltextExp extends Expression {
113113
const normalizedFlatText = normalizeSearchText(flatText);
114114

115115
// Check if =phrase appears in flatText (indicates attribute value match)
116-
matches = normalizedFlatText.includes(`=${normalizedPhrase}`);
116+
// For single words, use word-boundary matching to avoid substring matches
117+
if (!normalizedPhrase.includes(' ')) {
118+
// Single word: look for =word with word boundaries
119+
// Split by = to get attribute values, then check each value for exact word match
120+
const parts = normalizedFlatText.split('=');
121+
matches = parts.slice(1).some(part => this.exactWordMatch(normalizedPhrase, part));
122+
} else {
123+
// Multi-word phrase: check for substring match
124+
matches = normalizedFlatText.includes(`=${normalizedPhrase}`);
125+
}
117126

118127
if ((this.operator === "=" && matches) || (this.operator === "!=" && !matches)) {
119128
resultNoteSet.add(noteFromBecca);
@@ -124,6 +133,17 @@ class NoteContentFulltextExp extends Expression {
124133
return resultNoteSet;
125134
}
126135

136+
/**
137+
* Helper method to check if a single word appears as an exact match in text
138+
* @param wordToFind - The word to search for (should be normalized)
139+
* @param text - The text to search in (should be normalized)
140+
* @returns true if the word is found as an exact match (not substring)
141+
*/
142+
private exactWordMatch(wordToFind: string, text: string): boolean {
143+
const words = text.split(/\s+/);
144+
return words.some(word => word === wordToFind);
145+
}
146+
127147
/**
128148
* Checks if content contains the exact word (with word boundaries) or exact phrase
129149
* This is case-insensitive since content and token are already normalized
@@ -139,9 +159,8 @@ class NoteContentFulltextExp extends Expression {
139159
return normalizedContent.includes(normalizedToken);
140160
}
141161

142-
// For single words, split content into words and check for exact match
143-
const words = normalizedContent.split(/\s+/);
144-
return words.some(word => word === normalizedToken);
162+
// For single words, use exact word matching to avoid substring matches
163+
return this.exactWordMatch(normalizedToken, normalizedContent);
145164
}
146165

147166
/**
@@ -155,7 +174,14 @@ class NoteContentFulltextExp extends Expression {
155174
// Join tokens with single space to form the phrase
156175
const phrase = normalizedTokens.join(" ");
157176

158-
// Check if the phrase appears as a substring (consecutive words)
177+
// For single-word phrases, use word-boundary matching to avoid substring matches
178+
// e.g., "asd" should not match "asdfasdf"
179+
if (!phrase.includes(' ')) {
180+
// Single word: use exact word matching to avoid substring matches
181+
return this.exactWordMatch(phrase, normalizedContent);
182+
}
183+
184+
// For multi-word phrases, check if the phrase appears as consecutive words
159185
if (normalizedContent.includes(phrase)) {
160186
return true;
161187
}

0 commit comments

Comments
 (0)