mirror of
https://github.com/nix-community/noogle.git
synced 2025-01-08 09:42:18 +03:00
add fuzzy search (#32)
* add fuzzy search * fix debug logger * clean up
This commit is contained in:
parent
db000540b1
commit
c707bd74a7
@ -1,11 +1,12 @@
|
|||||||
import { Box } from "@mui/system";
|
import { Box } from "@mui/system";
|
||||||
import { useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { PageState } from "../../models/internals";
|
import { PageState } from "../../models/internals";
|
||||||
import { byQuery, byType, pipe } from "../../queries";
|
import { byType, pipe } from "../../queries";
|
||||||
import { DocItem } from "../../models/nix";
|
import { DocItem } from "../../models/nix";
|
||||||
import { BasicList, BasicListItem } from "../basicList";
|
import { BasicList, BasicListItem } from "../basicList";
|
||||||
import FunctionItem from "../functionItem/functionItem";
|
import FunctionItem from "../functionItem/functionItem";
|
||||||
import { SetPageStateVariable } from "../pageContext";
|
import { SetPageStateVariable } from "../pageContext";
|
||||||
|
import { useMiniSearch } from "react-minisearch";
|
||||||
|
|
||||||
interface FunctionsProps {
|
interface FunctionsProps {
|
||||||
pageState: PageState;
|
pageState: PageState;
|
||||||
@ -18,10 +19,36 @@ export function NixFunctions(props: FunctionsProps) {
|
|||||||
|
|
||||||
const setSelected = setPageStateVariable<string | null>("selected");
|
const setSelected = setPageStateVariable<string | null>("selected");
|
||||||
|
|
||||||
const filteredData = useMemo(
|
const { search, searchResults, rawResults } = useMiniSearch<DocItem>(data, {
|
||||||
() => pipe(byType(filter), byQuery(term))(data),
|
fields: ["id", "name", "category", "description", "example", "fn_type"],
|
||||||
[filter, term, data]
|
searchOptions: {
|
||||||
);
|
// allow 25% levenshtein distance (e.g. 2.5 of 10 characters don't match)
|
||||||
|
fuzzy: 0.25,
|
||||||
|
// prefer to show builtins first
|
||||||
|
boostDocument: (id, term) => {
|
||||||
|
let boost = 1;
|
||||||
|
boost += id.includes("builtins") ? 1 : 0;
|
||||||
|
boost += id.includes(term) ? 10 : 0;
|
||||||
|
return boost;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tokenize: (text: string): string[] => {
|
||||||
|
const tokens = text.split(/\W|(?=[A-Z])/);
|
||||||
|
return tokens;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
//initial site-load is safe to call
|
||||||
|
useEffect(() => {
|
||||||
|
search(term);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const filteredData = useMemo(() => {
|
||||||
|
const dataForFilter = term ? searchResults || [] : data;
|
||||||
|
console.debug({ rawResults });
|
||||||
|
return pipe(byType(filter))(dataForFilter);
|
||||||
|
}, [filter, term, rawResults, searchResults, data]);
|
||||||
|
|
||||||
const preRenderedItems: BasicListItem[] = filteredData.map(
|
const preRenderedItems: BasicListItem[] = filteredData.map(
|
||||||
(docItem: DocItem) => {
|
(docItem: DocItem) => {
|
||||||
@ -50,7 +77,7 @@ export function NixFunctions(props: FunctionsProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ ml: { xs: 0, md: 2 } }}>
|
<Box sx={{ ml: { xs: 0, md: 2 } }}>
|
||||||
<BasicList items={preRenderedItems} />
|
<BasicList items={preRenderedItems} search={search} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import { usePageContext } from "../pageContext";
|
|||||||
import { useMobile } from "../layout/layout";
|
import { useMobile } from "../layout/layout";
|
||||||
import { EmptyRecordsPlaceholder } from "../emptyRecordsPlaceholder";
|
import { EmptyRecordsPlaceholder } from "../emptyRecordsPlaceholder";
|
||||||
import { FunctionOfTheDay } from "../functionOfTheDay";
|
import { FunctionOfTheDay } from "../functionOfTheDay";
|
||||||
|
import { Query, SearchOptions } from "minisearch";
|
||||||
|
|
||||||
export type BasicListItem = {
|
export type BasicListItem = {
|
||||||
item: React.ReactNode;
|
item: React.ReactNode;
|
||||||
@ -15,12 +16,13 @@ export type BasicListItem = {
|
|||||||
};
|
};
|
||||||
export type BasicListProps = BasicDataViewProps & {
|
export type BasicListProps = BasicDataViewProps & {
|
||||||
selected?: string | null;
|
selected?: string | null;
|
||||||
|
search: (query: Query, options?: SearchOptions) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ViewMode = "explore" | "browse";
|
type ViewMode = "explore" | "browse";
|
||||||
|
|
||||||
export function BasicList(props: BasicListProps) {
|
export function BasicList(props: BasicListProps) {
|
||||||
const { items } = props;
|
const { items, search } = props;
|
||||||
const { pageState, setPageStateVariable, resetQueries } = usePageContext();
|
const { pageState, setPageStateVariable, resetQueries } = usePageContext();
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
const { page, itemsPerPage, filter, term, FOTD, data } = pageState;
|
const { page, itemsPerPage, filter, term, FOTD, data } = pageState;
|
||||||
@ -60,6 +62,16 @@ export function BasicList(props: BasicListProps) {
|
|||||||
|
|
||||||
const handleSearch = (term: string) => {
|
const handleSearch = (term: string) => {
|
||||||
setTerm(term);
|
setTerm(term);
|
||||||
|
search(term, {
|
||||||
|
boost: {
|
||||||
|
id: 10,
|
||||||
|
name: 8,
|
||||||
|
category: 6,
|
||||||
|
example: 0.5,
|
||||||
|
fn_type: 3,
|
||||||
|
description: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
setPage(1);
|
setPage(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
31
website/package-lock.json
generated
31
website/package-lock.json
generated
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "nextapp",
|
"name": "noogle",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "nextapp",
|
"name": "noogle",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.5",
|
"@emotion/react": "^11.10.5",
|
||||||
@ -19,12 +19,14 @@
|
|||||||
"eslint": "8.27.0",
|
"eslint": "8.27.0",
|
||||||
"eslint-config-next": "13.0.2",
|
"eslint-config-next": "13.0.2",
|
||||||
"highlight.js": "^11.7.0",
|
"highlight.js": "^11.7.0",
|
||||||
|
"minisearch": "^6.0.1",
|
||||||
"next": "13.0.2",
|
"next": "13.0.2",
|
||||||
"notistack": "^2.0.8",
|
"notistack": "^2.0.8",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-highlight": "^0.15.0",
|
"react-highlight": "^0.15.0",
|
||||||
"react-markdown": "^8.0.4",
|
"react-markdown": "^8.0.4",
|
||||||
|
"react-minisearch": "^6.0.2",
|
||||||
"rehype-highlight": "^6.0.0",
|
"rehype-highlight": "^6.0.0",
|
||||||
"seedrandom": "^3.0.5",
|
"seedrandom": "^3.0.5",
|
||||||
"typescript": "4.8.4",
|
"typescript": "4.8.4",
|
||||||
@ -3832,6 +3834,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minisearch": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Ly1w0nHKnlhAAh6/BF/+9NgzXfoJxaJ8nhopFhQ3NcvFJrFIL+iCg9gw9e9UMBD+XIsp/RyznJ/o5UIe5Kw+kg=="
|
||||||
|
},
|
||||||
"node_modules/mri": {
|
"node_modules/mri": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||||
@ -4334,6 +4341,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-minisearch": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-minisearch/-/react-minisearch-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-oBhLz2XEdQeneYCYaFCwtoZJbc5KqBQkWXp9O6l3O/woRxSWGcefYctzB9+LtQTiVNEMgo3FEvCnRFPFeVKXfg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"minisearch": "^6.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-transition-group": {
|
"node_modules/react-transition-group": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
@ -7693,6 +7709,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
|
||||||
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
|
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
|
||||||
},
|
},
|
||||||
|
"minisearch": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Ly1w0nHKnlhAAh6/BF/+9NgzXfoJxaJ8nhopFhQ3NcvFJrFIL+iCg9gw9e9UMBD+XIsp/RyznJ/o5UIe5Kw+kg=="
|
||||||
|
},
|
||||||
"mri": {
|
"mri": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||||
@ -8016,6 +8037,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-minisearch": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-minisearch/-/react-minisearch-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-oBhLz2XEdQeneYCYaFCwtoZJbc5KqBQkWXp9O6l3O/woRxSWGcefYctzB9+LtQTiVNEMgo3FEvCnRFPFeVKXfg==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"react-transition-group": {
|
"react-transition-group": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
@ -21,12 +21,14 @@
|
|||||||
"eslint": "8.27.0",
|
"eslint": "8.27.0",
|
||||||
"eslint-config-next": "13.0.2",
|
"eslint-config-next": "13.0.2",
|
||||||
"highlight.js": "^11.7.0",
|
"highlight.js": "^11.7.0",
|
||||||
|
"minisearch": "^6.0.1",
|
||||||
"next": "13.0.2",
|
"next": "13.0.2",
|
||||||
"notistack": "^2.0.8",
|
"notistack": "^2.0.8",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-highlight": "^0.15.0",
|
"react-highlight": "^0.15.0",
|
||||||
"react-markdown": "^8.0.4",
|
"react-markdown": "^8.0.4",
|
||||||
|
"react-minisearch": "^6.0.2",
|
||||||
"rehype-highlight": "^6.0.0",
|
"rehype-highlight": "^6.0.0",
|
||||||
"seedrandom": "^3.0.5",
|
"seedrandom": "^3.0.5",
|
||||||
"typescript": "4.8.4",
|
"typescript": "4.8.4",
|
||||||
|
@ -49,7 +49,7 @@ export default function FunctionsPage() {
|
|||||||
if (router.isReady && initialProps === null) {
|
if (router.isReady && initialProps === null) {
|
||||||
getInitialProps(router).then((r) => {
|
getInitialProps(router).then((r) => {
|
||||||
const { props } = r;
|
const { props } = r;
|
||||||
console.info("Url Query changed\n\nUpdating pageState with delta:", {
|
console.debug("Url Query changed\n\nUpdating pageState with delta:", {
|
||||||
props,
|
props,
|
||||||
});
|
});
|
||||||
setInitialProps((curr) => ({ ...curr, ...props }));
|
setInitialProps((curr) => ({ ...curr, ...props }));
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { DocItem, MetaData } from "../models/nix";
|
import { DocItem, MetaData } from "../models/nix";
|
||||||
|
// import MiniSearch from 'minisearch'
|
||||||
|
|
||||||
|
// export const byMinisearch = (term: string, miniSearch: MiniSearch<DocItem> ) => (data: MetaData): MetaData => {
|
||||||
|
// return miniSearch.search(term);
|
||||||
|
// }
|
||||||
|
|
||||||
export const byQuery =
|
export const byQuery =
|
||||||
(rawTerm: string) =>
|
(rawTerm: string) =>
|
||||||
|
Loading…
Reference in New Issue
Block a user