mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 07:08:55 +02:00
feat(quick_search): make sure that we rank exact matches higher when merging results with fuzzy search
This commit is contained in:
parent
db1619af31
commit
583ab8dc92
@ -81,7 +81,7 @@ describe("Progressive Search Strategy", () => {
|
||||
expect(findNoteByTitle(searchResults, "Anaylsis Three")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should merge exact and fuzzy results with exact matches ranked higher", () => {
|
||||
it("should merge exact and fuzzy results with exact matches always ranked higher", () => {
|
||||
rootNote
|
||||
.child(note("Analysis Report")) // Exact match
|
||||
.child(note("Data Analysis")) // Exact match
|
||||
@ -93,26 +93,26 @@ describe("Progressive Search Strategy", () => {
|
||||
|
||||
expect(searchResults.length).toBe(4);
|
||||
|
||||
// First two results should be exact matches with higher scores
|
||||
const exactMatches = ["Analysis Report", "Data Analysis"];
|
||||
const fuzzyMatches = ["Anaylsis Doc", "Statistical Anlaysis"];
|
||||
// Get the note titles in result order
|
||||
const resultTitles = searchResults.map(r => becca.notes[r.noteId].title);
|
||||
|
||||
// Find exact and fuzzy match results
|
||||
const exactResults = searchResults.filter(result =>
|
||||
exactMatches.includes(becca.notes[result.noteId].title)
|
||||
);
|
||||
const fuzzyResults = searchResults.filter(result =>
|
||||
fuzzyMatches.includes(becca.notes[result.noteId].title)
|
||||
);
|
||||
// Find positions of exact and fuzzy matches
|
||||
const exactPositions = resultTitles.map((title, index) =>
|
||||
title.toLowerCase().includes("analysis") ? index : -1
|
||||
).filter(pos => pos !== -1);
|
||||
|
||||
expect(exactResults.length).toBe(2);
|
||||
expect(fuzzyResults.length).toBe(2);
|
||||
const fuzzyPositions = resultTitles.map((title, index) =>
|
||||
(title.includes("Anaylsis") || title.includes("Anlaysis")) ? index : -1
|
||||
).filter(pos => pos !== -1);
|
||||
|
||||
// Exact matches should have higher scores than fuzzy matches
|
||||
const lowestExactScore = Math.min(...exactResults.map(r => r.score));
|
||||
const highestFuzzyScore = Math.max(...fuzzyResults.map(r => r.score));
|
||||
expect(exactPositions.length).toBe(2);
|
||||
expect(fuzzyPositions.length).toBe(2);
|
||||
|
||||
expect(lowestExactScore).toBeGreaterThan(highestFuzzyScore);
|
||||
// CRITICAL: All exact matches must come before all fuzzy matches
|
||||
const lastExactPosition = Math.max(...exactPositions);
|
||||
const firstFuzzyPosition = Math.min(...fuzzyPositions);
|
||||
|
||||
expect(lastExactPosition).toBeLessThan(firstFuzzyPosition);
|
||||
});
|
||||
|
||||
it("should not duplicate results between phases", () => {
|
||||
|
@ -578,7 +578,7 @@ describe("Search", () => {
|
||||
expect(searchResults.length).toEqual(10);
|
||||
});
|
||||
|
||||
it("progressive search prioritizes exact matches over fuzzy matches", () => {
|
||||
it("progressive search always puts exact matches before fuzzy matches", () => {
|
||||
rootNote
|
||||
.child(note("Analysis Report")) // Exact match
|
||||
.child(note("Data Analysis")) // Exact match
|
||||
@ -591,27 +591,30 @@ describe("Search", () => {
|
||||
const searchContext = new SearchContext();
|
||||
const searchResults = searchService.findResultsWithQuery("analysis", searchContext);
|
||||
|
||||
// Should find all matches but exact ones should rank higher
|
||||
// Should find all matches but exact ones should come first
|
||||
expect(searchResults.length).toEqual(7);
|
||||
|
||||
// First 5 results should be exact matches with higher scores
|
||||
const topResults = searchResults.slice(0, 5);
|
||||
const bottomResults = searchResults.slice(5);
|
||||
// Get note titles in result order
|
||||
const resultTitles = searchResults.map(r => becca.notes[r.noteId].title);
|
||||
|
||||
const topTitles = topResults.map(r => becca.notes[r.noteId].title);
|
||||
const bottomTitles = bottomResults.map(r => becca.notes[r.noteId].title);
|
||||
// Find all exact matches (contain "analysis")
|
||||
const exactMatchIndices = resultTitles.map((title, index) =>
|
||||
title.toLowerCase().includes("analysis") ? index : -1
|
||||
).filter(index => index !== -1);
|
||||
|
||||
// All top results should be exact matches
|
||||
expect(topTitles.every(title => title.toLowerCase().includes("analysis"))).toBeTruthy();
|
||||
// Find all fuzzy matches (contain typos)
|
||||
const fuzzyMatchIndices = resultTitles.map((title, index) =>
|
||||
(title.includes("Anaylsis") || title.includes("Anlaysis")) ? index : -1
|
||||
).filter(index => index !== -1);
|
||||
|
||||
// Bottom results should be fuzzy matches
|
||||
expect(bottomTitles.some(title => title.includes("Anaylsis") || title.includes("Anlaysis"))).toBeTruthy();
|
||||
expect(exactMatchIndices.length).toEqual(5);
|
||||
expect(fuzzyMatchIndices.length).toEqual(2);
|
||||
|
||||
// Verify score ordering
|
||||
const lowestExactScore = Math.min(...topResults.map(r => r.score));
|
||||
const highestFuzzyScore = Math.max(...bottomResults.map(r => r.score));
|
||||
// CRITICAL: All exact matches must appear before all fuzzy matches
|
||||
const lastExactIndex = Math.max(...exactMatchIndices);
|
||||
const firstFuzzyIndex = Math.min(...fuzzyMatchIndices);
|
||||
|
||||
expect(lowestExactScore).toBeGreaterThan(highestFuzzyScore);
|
||||
expect(lastExactIndex).toBeLessThan(firstFuzzyIndex);
|
||||
});
|
||||
|
||||
|
||||
|
@ -317,11 +317,8 @@ function mergeExactAndFuzzyResults(exactResults: SearchResult[], fuzzyResults: S
|
||||
// Add fuzzy results that aren't already in exact results
|
||||
const additionalFuzzyResults = fuzzyResults.filter(result => !exactNoteIds.has(result.noteId));
|
||||
|
||||
// Combine results with exact matches first, then fuzzy matches
|
||||
const combinedResults = [...exactResults, ...additionalFuzzyResults];
|
||||
|
||||
// Sort combined results by score
|
||||
combinedResults.sort((a, b) => {
|
||||
// Sort exact results by score (best exact matches first)
|
||||
exactResults.sort((a, b) => {
|
||||
if (a.score > b.score) {
|
||||
return -1;
|
||||
} else if (a.score < b.score) {
|
||||
@ -336,7 +333,24 @@ function mergeExactAndFuzzyResults(exactResults: SearchResult[], fuzzyResults: S
|
||||
return a.notePathArray.length < b.notePathArray.length ? -1 : 1;
|
||||
});
|
||||
|
||||
return combinedResults;
|
||||
// Sort fuzzy results by score (best fuzzy matches first)
|
||||
additionalFuzzyResults.sort((a, b) => {
|
||||
if (a.score > b.score) {
|
||||
return -1;
|
||||
} else if (a.score < b.score) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// if score does not decide then sort results by depth of the note.
|
||||
if (a.notePathArray.length === b.notePathArray.length) {
|
||||
return a.notePathTitle < b.notePathTitle ? -1 : 1;
|
||||
}
|
||||
|
||||
return a.notePathArray.length < b.notePathArray.length ? -1 : 1;
|
||||
});
|
||||
|
||||
// CRITICAL: Always put exact matches before fuzzy matches, regardless of scores
|
||||
return [...exactResults, ...additionalFuzzyResults];
|
||||
}
|
||||
|
||||
function parseQueryToExpression(query: string, searchContext: SearchContext) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user