* Replaces old Deno server with a Go implementation * Restructured the code base accordingly
88 lines
2.3 KiB
TypeScript
88 lines
2.3 KiB
TypeScript
// @deno-types="https://deno.land/x/fuse@v6.4.1/dist/fuse.d.ts"
|
|
import Fuse from "fuse";
|
|
import type { FilterOption } from "@silverbulletmd/silverbullet/type/client";
|
|
import { fileName } from "@silverbulletmd/silverbullet/lib/resolve";
|
|
|
|
type FuseOption = FilterOption & {
|
|
baseName: string;
|
|
};
|
|
|
|
export const fuzzySearchAndSort = (
|
|
arr: FilterOption[],
|
|
searchPhrase: string,
|
|
): FilterOption[] => {
|
|
if (!searchPhrase) {
|
|
return arr.sort((a, b) => (a.orderId || 0) - (b.orderId || 0));
|
|
}
|
|
|
|
const enrichedArr: FuseOption[] = arr.map((item) => {
|
|
return {
|
|
...item,
|
|
// Only relevant for pages and or documents and not commands
|
|
baseName: fileName(item.name),
|
|
displayName: item?.meta?.displayName,
|
|
aliases: item?.meta?.aliases?.join(" "),
|
|
category: item.category,
|
|
};
|
|
});
|
|
|
|
const fuse = new Fuse(enrichedArr, {
|
|
keys: [{
|
|
name: "name",
|
|
weight: 0.3,
|
|
}, {
|
|
name: "baseName",
|
|
weight: 1,
|
|
}, {
|
|
name: "displayName",
|
|
weight: 0.7,
|
|
}, {
|
|
name: "aliases",
|
|
weight: 0.5,
|
|
}, {
|
|
name: "description",
|
|
weight: 0.3,
|
|
}, {
|
|
name: "category",
|
|
weight: 0.8,
|
|
}],
|
|
includeScore: true,
|
|
shouldSort: true,
|
|
isCaseSensitive: false,
|
|
ignoreLocation: true,
|
|
threshold: 0.6,
|
|
sortFn: (a, b): number => {
|
|
const aItem = enrichedArr[a.idx];
|
|
const bItem = enrichedArr[b.idx];
|
|
|
|
// Check for exact category matches first
|
|
const searchLower = searchPhrase.toLowerCase();
|
|
const aExactCategoryMatch = aItem.category?.toLowerCase() === searchLower;
|
|
const bExactCategoryMatch = bItem.category?.toLowerCase() === searchLower;
|
|
|
|
if (aExactCategoryMatch && !bExactCategoryMatch) {
|
|
return -1; // a comes first
|
|
}
|
|
if (!aExactCategoryMatch && bExactCategoryMatch) {
|
|
return 1; // b comes first
|
|
}
|
|
|
|
// If both or neither have exact category matches, use normal scoring
|
|
if (a.score === b.score) {
|
|
const aOrder = aItem.orderId || 0;
|
|
const bOrder = bItem.orderId || 0;
|
|
if (aOrder !== bOrder) {
|
|
return aOrder - bOrder;
|
|
}
|
|
}
|
|
return a.score - b.score;
|
|
},
|
|
});
|
|
|
|
const results = fuse.search(searchPhrase);
|
|
return results.map((r) => ({
|
|
...r.item,
|
|
fuseScore: r.score,
|
|
}));
|
|
};
|