mirror of
https://github.com/zadam/trilium.git
synced 2025-10-21 07:38:53 +02:00
feat(search): also implement defensive checks for undefined notes
This commit is contained in:
parent
583ab8dc92
commit
5b669ca287
@ -57,7 +57,11 @@ class NoteFlatTextExp extends Expression {
|
|||||||
const foundAttrTokens: string[] = [];
|
const foundAttrTokens: string[] = [];
|
||||||
|
|
||||||
for (const token of remainingTokens) {
|
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);
|
foundAttrTokens.push(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,7 +109,11 @@ class NoteFlatTextExp extends Expression {
|
|||||||
const foundAttrTokens: string[] = [];
|
const foundAttrTokens: string[] = [];
|
||||||
|
|
||||||
for (const token of this.tokens) {
|
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);
|
foundAttrTokens.push(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,25 +25,25 @@ describe("Progressive Search Strategy", () => {
|
|||||||
it("should complete search with exact matches when sufficient results found", () => {
|
it("should complete search with exact matches when sufficient results found", () => {
|
||||||
// Create notes with exact matches
|
// Create notes with exact matches
|
||||||
rootNote
|
rootNote
|
||||||
.child(note("Test Document One"))
|
.child(note("Document Analysis One"))
|
||||||
.child(note("Test Report Two"))
|
.child(note("Document Report Two"))
|
||||||
.child(note("Test Analysis Three"))
|
.child(note("Document Review Three"))
|
||||||
.child(note("Test Summary Four"))
|
.child(note("Document Summary Four"))
|
||||||
.child(note("Test Review Five"))
|
.child(note("Document Overview Five"))
|
||||||
.child(note("Typo Test Documnt")); // This has a typo
|
.child(note("Documnt Analysis Six")); // This has a typo that should require fuzzy matching
|
||||||
|
|
||||||
const searchContext = new SearchContext();
|
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
|
// Should find 5 exact matches and not need fuzzy matching
|
||||||
expect(searchResults.length).toBeGreaterThanOrEqual(5);
|
expect(searchResults.length).toEqual(5);
|
||||||
|
|
||||||
// Verify all results have high scores (exact matches)
|
// Verify all results have high scores (exact matches)
|
||||||
const highQualityResults = searchResults.filter(result => result.score >= 10);
|
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
|
// 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", () => {
|
it("should use exact match scoring only in Phase 1", () => {
|
||||||
|
@ -583,16 +583,15 @@ describe("Search", () => {
|
|||||||
.child(note("Analysis Report")) // Exact match
|
.child(note("Analysis Report")) // Exact match
|
||||||
.child(note("Data Analysis")) // Exact match
|
.child(note("Data Analysis")) // Exact match
|
||||||
.child(note("Test 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("Advanced Anaylsis")) // Fuzzy match (typo)
|
||||||
.child(note("Quick Anlaysis")); // Fuzzy match (typo)
|
.child(note("Quick Anlaysis")); // Fuzzy match (typo)
|
||||||
|
|
||||||
const searchContext = new SearchContext();
|
const searchContext = new SearchContext();
|
||||||
const searchResults = searchService.findResultsWithQuery("analysis", searchContext);
|
const searchResults = searchService.findResultsWithQuery("analysis", searchContext);
|
||||||
|
|
||||||
// Should find all matches but exact ones should come first
|
// With only 3 exact matches (below threshold), fuzzy should be triggered
|
||||||
expect(searchResults.length).toEqual(7);
|
// Should find all 5 matches but exact ones should come first
|
||||||
|
expect(searchResults.length).toEqual(5);
|
||||||
|
|
||||||
// Get note titles in result order
|
// Get note titles in result order
|
||||||
const resultTitles = searchResults.map(r => becca.notes[r.noteId].title);
|
const resultTitles = searchResults.map(r => becca.notes[r.noteId].title);
|
||||||
@ -607,7 +606,7 @@ describe("Search", () => {
|
|||||||
(title.includes("Anaylsis") || title.includes("Anlaysis")) ? index : -1
|
(title.includes("Anaylsis") || title.includes("Anlaysis")) ? index : -1
|
||||||
).filter(index => index !== -1);
|
).filter(index => index !== -1);
|
||||||
|
|
||||||
expect(exactMatchIndices.length).toEqual(5);
|
expect(exactMatchIndices.length).toEqual(3);
|
||||||
expect(fuzzyMatchIndices.length).toEqual(2);
|
expect(fuzzyMatchIndices.length).toEqual(2);
|
||||||
|
|
||||||
// CRITICAL: All exact matches must appear before all fuzzy matches
|
// CRITICAL: All exact matches must appear before all fuzzy matches
|
||||||
|
@ -237,6 +237,19 @@ function findResultsWithExpression(expression: Expression, searchContext: Search
|
|||||||
loadNeededInfoFromDatabase();
|
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)
|
// Phase 1: Try exact matches first (without fuzzy matching)
|
||||||
const exactResults = performSearch(expression, searchContext, false);
|
const exactResults = performSearch(expression, searchContext, false);
|
||||||
|
|
||||||
@ -251,7 +264,7 @@ function findResultsWithExpression(expression: Expression, searchContext: Search
|
|||||||
return exactResults;
|
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);
|
const fuzzyResults = performSearch(expression, searchContext, true);
|
||||||
|
|
||||||
// Merge results, ensuring exact matches always rank higher than fuzzy matches
|
// Merge results, ensuring exact matches always rank higher than fuzzy matches
|
||||||
@ -402,6 +415,16 @@ function findResultsWithQuery(query: string, searchContext: SearchContext): Sear
|
|||||||
return [];
|
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);
|
return findResultsWithExpression(expression, searchContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user