Skip to content

Commit 5b669ca

Browse files
committed
feat(search): also implement defensive checks for undefined notes
1 parent 583ab8d commit 5b669ca

File tree

4 files changed

+49
-19
lines changed

4 files changed

+49
-19
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ class NoteFlatTextExp extends Expression {
5757
const foundAttrTokens: string[] = [];
5858

5959
for (const token of remainingTokens) {
60-
if (note.type.includes(token) || note.mime.includes(token)) {
60+
// Add defensive checks for undefined properties
61+
const typeMatches = note.type && note.type.includes(token);
62+
const mimeMatches = note.mime && note.mime.includes(token);
63+
64+
if (typeMatches || mimeMatches) {
6165
foundAttrTokens.push(token);
6266
}
6367
}
@@ -105,7 +109,11 @@ class NoteFlatTextExp extends Expression {
105109
const foundAttrTokens: string[] = [];
106110

107111
for (const token of this.tokens) {
108-
if (note.type.includes(token) || note.mime.includes(token)) {
112+
// Add defensive checks for undefined properties
113+
const typeMatches = note.type && note.type.includes(token);
114+
const mimeMatches = note.mime && note.mime.includes(token);
115+
116+
if (typeMatches || mimeMatches) {
109117
foundAttrTokens.push(token);
110118
}
111119

apps/server/src/services/search/services/progressive_search.spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,25 @@ describe("Progressive Search Strategy", () => {
2525
it("should complete search with exact matches when sufficient results found", () => {
2626
// Create notes with exact matches
2727
rootNote
28-
.child(note("Test Document One"))
29-
.child(note("Test Report Two"))
30-
.child(note("Test Analysis Three"))
31-
.child(note("Test Summary Four"))
32-
.child(note("Test Review Five"))
33-
.child(note("Typo Test Documnt")); // This has a typo
28+
.child(note("Document Analysis One"))
29+
.child(note("Document Report Two"))
30+
.child(note("Document Review Three"))
31+
.child(note("Document Summary Four"))
32+
.child(note("Document Overview Five"))
33+
.child(note("Documnt Analysis Six")); // This has a typo that should require fuzzy matching
3434

3535
const searchContext = new SearchContext();
36-
const searchResults = searchService.findResultsWithQuery("test", searchContext);
36+
const searchResults = searchService.findResultsWithQuery("document", searchContext);
3737

38-
// Should find 5+ exact matches and not process the typo
39-
expect(searchResults.length).toBeGreaterThanOrEqual(5);
38+
// Should find 5 exact matches and not need fuzzy matching
39+
expect(searchResults.length).toEqual(5);
4040

4141
// Verify all results have high scores (exact matches)
4242
const highQualityResults = searchResults.filter(result => result.score >= 10);
43-
expect(highQualityResults.length).toBeGreaterThanOrEqual(5);
43+
expect(highQualityResults.length).toEqual(5);
4444

4545
// The typo document should not be in results since we have enough exact matches
46-
expect(findNoteByTitle(searchResults, "Typo Test Documnt")).toBeFalsy();
46+
expect(findNoteByTitle(searchResults, "Documnt Analysis Six")).toBeFalsy();
4747
});
4848

4949
it("should use exact match scoring only in Phase 1", () => {

apps/server/src/services/search/services/search.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -583,16 +583,15 @@ describe("Search", () => {
583583
.child(note("Analysis Report")) // Exact match
584584
.child(note("Data Analysis")) // Exact match
585585
.child(note("Test Analysis")) // Exact match
586-
.child(note("Statistical Analysis")) // Exact match
587-
.child(note("Business Analysis")) // Exact match
588586
.child(note("Advanced Anaylsis")) // Fuzzy match (typo)
589587
.child(note("Quick Anlaysis")); // Fuzzy match (typo)
590588

591589
const searchContext = new SearchContext();
592590
const searchResults = searchService.findResultsWithQuery("analysis", searchContext);
593591

594-
// Should find all matches but exact ones should come first
595-
expect(searchResults.length).toEqual(7);
592+
// With only 3 exact matches (below threshold), fuzzy should be triggered
593+
// Should find all 5 matches but exact ones should come first
594+
expect(searchResults.length).toEqual(5);
596595

597596
// Get note titles in result order
598597
const resultTitles = searchResults.map(r => becca.notes[r.noteId].title);
@@ -607,7 +606,7 @@ describe("Search", () => {
607606
(title.includes("Anaylsis") || title.includes("Anlaysis")) ? index : -1
608607
).filter(index => index !== -1);
609608

610-
expect(exactMatchIndices.length).toEqual(5);
609+
expect(exactMatchIndices.length).toEqual(3);
611610
expect(fuzzyMatchIndices.length).toEqual(2);
612611

613612
// CRITICAL: All exact matches must appear before all fuzzy matches

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,19 @@ function findResultsWithExpression(expression: Expression, searchContext: Search
237237
loadNeededInfoFromDatabase();
238238
}
239239

240+
// If there's an explicit orderBy clause, skip progressive search
241+
// as it would interfere with the ordering
242+
if (searchContext.orderBy) {
243+
// For ordered queries, don't use progressive search but respect
244+
// the original fuzzy matching setting
245+
return performSearch(expression, searchContext, searchContext.enableFuzzyMatching);
246+
}
247+
248+
// If fuzzy matching is explicitly disabled, skip progressive search
249+
if (!searchContext.enableFuzzyMatching) {
250+
return performSearch(expression, searchContext, false);
251+
}
252+
240253
// Phase 1: Try exact matches first (without fuzzy matching)
241254
const exactResults = performSearch(expression, searchContext, false);
242255

@@ -251,7 +264,7 @@ function findResultsWithExpression(expression: Expression, searchContext: Search
251264
return exactResults;
252265
}
253266

254-
// Phase 2: Add fuzzy matching as fallback
267+
// Phase 2: Add fuzzy matching as fallback when exact matches are insufficient
255268
const fuzzyResults = performSearch(expression, searchContext, true);
256269

257270
// Merge results, ensuring exact matches always rank higher than fuzzy matches
@@ -402,6 +415,16 @@ function findResultsWithQuery(query: string, searchContext: SearchContext): Sear
402415
return [];
403416
}
404417

418+
// If the query starts with '#', it's a pure expression query.
419+
// Don't use progressive search for these as they may have complex
420+
// ordering or other logic that shouldn't be interfered with.
421+
const isPureExpressionQuery = query.trim().startsWith('#');
422+
423+
if (isPureExpressionQuery) {
424+
// For pure expression queries, use standard search without progressive phases
425+
return performSearch(expression, searchContext, searchContext.enableFuzzyMatching);
426+
}
427+
405428
return findResultsWithExpression(expression, searchContext);
406429
}
407430

0 commit comments

Comments
 (0)