diff --git a/plugs/core/page.ts b/plugs/core/page.ts index 253c3c4b..c760e230 100644 --- a/plugs/core/page.ts +++ b/plugs/core/page.ts @@ -57,7 +57,7 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) { delete pageMeta[key]; } } - console.log("Extracted page meta data", pageMeta); + // console.log("Extracted page meta data", pageMeta); await index.set(name, "meta:", pageMeta); } diff --git a/plugs/directive/complete.ts b/plugs/directive/complete.ts index 3ac967e9..833d0c94 100644 --- a/plugs/directive/complete.ts +++ b/plugs/directive/complete.ts @@ -4,7 +4,7 @@ import { buildHandebarOptions } from "./util.ts"; import type { PageMeta } from "../../web/types.ts"; export async function queryComplete(completeEvent: CompleteEvent) { - const match = /#query ([\w\-_]+)*$/.exec(completeEvent.linePrefix); + const match = /#query ([\w\-_]*)$/.exec(completeEvent.linePrefix); if (!match) { return null; } diff --git a/web/components/filter.tsx b/web/components/filter.tsx index 6f0d99d9..ebd8aa5e 100644 --- a/web/components/filter.tsx +++ b/web/components/filter.tsx @@ -9,11 +9,7 @@ import { FilterOption } from "../types.ts"; import { FunctionalComponent } from "https://esm.sh/v99/preact@10.11.3/src/index"; import { FeatherProps } from "https://esm.sh/v99/preact-feather@4.2.1/dist/types"; import { MiniEditor } from "./mini_editor.tsx"; -import { fuzzySearchAndSort } from "./fuzzy_search.ts"; - -type FilterResult = FilterOption & { - result?: any; -}; +import { fuzzySearchAndSort } from "./fuse_search.ts"; export function FilterList({ placeholder, diff --git a/web/components/fuse_search.test.ts b/web/components/fuse_search.test.ts new file mode 100644 index 00000000..a163776e --- /dev/null +++ b/web/components/fuse_search.test.ts @@ -0,0 +1,25 @@ +import { FilterOption } from "../types.ts"; +import { assertEquals } from "../../test_deps.ts"; +import { fuzzySearchAndSort } from "./fuse_search.ts"; + +Deno.test("testFuzzyFilter", () => { + const array: FilterOption[] = [ + { name: "My Company/Hank", orderId: 2 }, + { name: "My Company/Hane", orderId: 1 }, + { name: "My Company/Steve Co" }, + { name: "Other/Steve" }, + { name: "Steve" }, + ]; + + // Prioritize match in last path part + let results = fuzzySearchAndSort(array, ""); + assertEquals(results.length, array.length); + results = fuzzySearchAndSort(array, "Steve"); + assertEquals(results.length, 3); + results = fuzzySearchAndSort(array, "Co"); + // Match in last path part + assertEquals(results[0].name, "My Company/Steve Co"); + // Due to orderId + assertEquals(results[1].name, "My Company/Hane"); + assertEquals(results[2].name, "My Company/Hank"); +}); diff --git a/web/components/fuse_search.ts b/web/components/fuse_search.ts new file mode 100644 index 00000000..f5a6cd66 --- /dev/null +++ b/web/components/fuse_search.ts @@ -0,0 +1,47 @@ +// @deno-types="https://deno.land/x/fuse@v6.4.1/dist/fuse.d.ts" +import Fuse from "https://deno.land/x/fuse@v6.4.1/dist/fuse.esm.min.js"; +import { FilterOption } from "../types.ts"; + +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, baseName: item.name.split("/").pop()! }; + }); + const fuse = new Fuse(enrichedArr, { + keys: [{ + name: "name", + weight: 0.3, + }, { + name: "baseName", + weight: 0.7, + }], + includeScore: true, + shouldSort: true, + isCaseSensitive: false, + threshold: 0.6, + sortFn: (a, b): number => { + // console.log(a, b); + if (a.score === b.score) { + const aOrder = enrichedArr[a.idx].orderId || 0; + const bOrder = enrichedArr[b.idx].orderId || 0; + if (aOrder !== bOrder) { + return aOrder - bOrder; + } + } + return a.score - b.score; + }, + }); + + const results = fuse.search(searchPhrase); + // console.log("results", results); + return results.map((r) => r.item); +};