feat(search): also implement defensive checks for undefined notes

This commit is contained in:
perf3ct 2025-08-10 21:59:20 +00:00
parent 583ab8dc92
commit 5b669ca287
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232
4 changed files with 49 additions and 19 deletions

View File

@ -57,7 +57,11 @@ class NoteFlatTextExp extends Expression {
const foundAttrTokens: string[] = [];
for (const token of remainingTokens) {
if (note.type.includes(token) || note.mime.includes(token)) {
// Add defensive checks for undefined properties
const typeMatches = note.type && note.type.includes(token);
const mimeMatches = note.mime && note.mime.includes(token);
if (typeMatches || mimeMatches) {
foundAttrTokens.push(token);
}
}
@ -105,7 +109,11 @@ class NoteFlatTextExp extends Expression {
const foundAttrTokens: string[] = [];
for (const token of this.tokens) {
if (note.type.includes(token) || note.mime.includes(token)) {
// Add defensive checks for undefined properties
const typeMatches = note.type && note.type.includes(token);
const mimeMatches = note.mime && note.mime.includes(token);
if (typeMatches || mimeMatches) {
foundAttrTokens.push(token);
}

View File

@ -25,25 +25,25 @@ describe("Progressive Search Strategy", () => {
it("should complete search with exact matches when sufficient results found", () => {
// Create notes with exact matches
rootNote
.child(note("Test Document One"))
.child(note("Test Report Two"))
.child(note("Test Analysis Three"))
.child(note("Test Summary Four"))
.child(note("Test Review Five"))
.child(note("Typo Test Documnt")); // This has a typo
.child(note("Document Analysis One"))
.child(note("Document Report Two"))
.child(note("Document Review Three"))
.child(note("Document Summary Four"))
.child(note("Document Overview Five"))
.child(note("Documnt Analysis Six")); // This has a typo that should require fuzzy matching
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("test", searchContext);
const searchResults = searchService.findResultsWithQuery("document", searchContext);
// Should find 5+ exact matches and not process the typo
expect(searchResults.length).toBeGreaterThanOrEqual(5);
// Should find 5 exact matches and not need fuzzy matching
expect(searchResults.length).toEqual(5);
// Verify all results have high scores (exact matches)
const highQualityResults = searchResults.filter(result => result.score >= 10);
expect(highQualityResults.length).toBeGreaterThanOrEqual(5);
expect(highQualityResults.length).toEqual(5);
// The typo document should not be in results since we have enough exact matches
expect(findNoteByTitle(searchResults, "Typo Test Documnt")).toBeFalsy();
expect(findNoteByTitle(searchResults, "Documnt Analysis Six")).toBeFalsy();
});
it("should use exact match scoring only in Phase 1", () => {

View File

@ -583,16 +583,15 @@ describe("Search", () => {
.child(note("Analysis Report")) // Exact match
.child(note("Data Analysis")) // Exact match
.child(note("Test Analysis")) // Exact match
.child(note("Statistical Analysis")) // Exact match
.child(note("Business Analysis")) // Exact match
.child(note("Advanced Anaylsis")) // Fuzzy match (typo)
.child(note("Quick Anlaysis")); // Fuzzy match (typo)
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("analysis", searchContext);
// Should find all matches but exact ones should come first
expect(searchResults.length).toEqual(7);
// With only 3 exact matches (below threshold), fuzzy should be triggered
// Should find all 5 matches but exact ones should come first
expect(searchResults.length).toEqual(5);
// Get note titles in result order
const resultTitles = searchResults.map(r => becca.notes[r.noteId].title);
@ -607,7 +606,7 @@ describe("Search", () => {
(title.includes("Anaylsis") || title.includes("Anlaysis")) ? index : -1
).filter(index => index !== -1);
expect(exactMatchIndices.length).toEqual(5);
expect(exactMatchIndices.length).toEqual(3);
expect(fuzzyMatchIndices.length).toEqual(2);
// CRITICAL: All exact matches must appear before all fuzzy matches

View File

@ -237,6 +237,19 @@ function findResultsWithExpression(expression: Expression, searchContext: Search
loadNeededInfoFromDatabase();
}
// If there's an explicit orderBy clause, skip progressive search
// as it would interfere with the ordering
if (searchContext.orderBy) {
// For ordered queries, don't use progressive search but respect
// the original fuzzy matching setting
return performSearch(expression, searchContext, searchContext.enableFuzzyMatching);
}
// If fuzzy matching is explicitly disabled, skip progressive search
if (!searchContext.enableFuzzyMatching) {
return performSearch(expression, searchContext, false);
}
// Phase 1: Try exact matches first (without fuzzy matching)
const exactResults = performSearch(expression, searchContext, false);
@ -251,7 +264,7 @@ function findResultsWithExpression(expression: Expression, searchContext: Search
return exactResults;
}
// Phase 2: Add fuzzy matching as fallback
// Phase 2: Add fuzzy matching as fallback when exact matches are insufficient
const fuzzyResults = performSearch(expression, searchContext, true);
// Merge results, ensuring exact matches always rank higher than fuzzy matches
@ -402,6 +415,16 @@ function findResultsWithQuery(query: string, searchContext: SearchContext): Sear
return [];
}
// If the query starts with '#', it's a pure expression query.
// Don't use progressive search for these as they may have complex
// ordering or other logic that shouldn't be interfered with.
const isPureExpressionQuery = query.trim().startsWith('#');
if (isPureExpressionQuery) {
// For pure expression queries, use standard search without progressive phases
return performSearch(expression, searchContext, searchContext.enableFuzzyMatching);
}
return findResultsWithExpression(expression, searchContext);
}