Skip to content

Commit 30bcceb

Browse files
add fuzzy search to ToolOntologies view; refactor search utilities
Removes unneeded parameters from functions, improves typing and definitions of variables in appropriate places. The `searchToolsByKeys` function has been separated and a `searchObjectsByKeys` function has been added which is now very reusable anywhere!
1 parent 2ae8583 commit 30bcceb

9 files changed

Lines changed: 217 additions & 117 deletions

File tree

client/src/components/Panels/Common/ToolSearch.vue

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,14 @@ import { storeToRefs } from "pinia";
44
import { nextTick } from "vue";
55
import { onMounted, onUnmounted, type PropType, watch } from "vue";
66
7-
import { FAVORITES_KEYS, searchToolsByKeys } from "@/components/Panels/utilities";
7+
import { FAVORITES_KEYS, searchTools } from "@/components/Panels/utilities";
88
import { type Tool, type ToolSection, useToolStore } from "@/stores/toolStore";
99
import { useUserStore } from "@/stores/userStore";
1010
import _l from "@/utils/localization";
1111
12-
import type { ToolSearchKeys } from "../utilities";
13-
1412
import DelayedInput from "@/components/Common/DelayedInput.vue";
1513
import LoadingSpan from "@/components/LoadingSpan.vue";
1614
17-
// Note: These are ordered by result priority (exact matches very top; words matches very bottom)
18-
const KEYS: ToolSearchKeys = { exact: 5, startsWith: 4, name: 3, description: 2, combined: 1, wordMatch: 0 };
19-
2015
const MIN_QUERY_LENGTH = 3;
2116
2217
const props = defineProps({
@@ -64,17 +59,15 @@ const { currentFavorites } = storeToRefs(useUserStore());
6459
const toolStore = useToolStore();
6560
const { searchWorker } = storeToRefs(toolStore);
6661
67-
interface RequestPaylod {
62+
interface RequestPayload {
6863
tools: Tool[];
69-
keys: ToolSearchKeys;
7064
query: string;
71-
panelView: string;
7265
currentPanel: Record<string, Tool | ToolSection>;
7366
}
7467
7568
interface SearchEventQuery {
76-
type: "searchToolsByKeys";
77-
payload: RequestPaylod;
69+
type: "searchTools";
70+
payload: RequestPayload;
7871
}
7972
8073
interface SearchEventClear {
@@ -116,9 +109,9 @@ interface ResponsePayload {
116109
117110
function handlePost(event: SearchEvent) {
118111
const { type } = event.data;
119-
if (type === "searchToolsByKeys") {
120-
const { tools, keys, query, panelView, currentPanel } = event.data.payload;
121-
const { results, resultPanel, closestTerm } = searchToolsByKeys(tools, keys, query, panelView, currentPanel);
112+
if (type === "searchTools") {
113+
const { tools, query, currentPanel } = event.data.payload;
114+
const { results, resultPanel, closestTerm } = searchTools(tools, query, currentPanel);
122115
// send the result back to the main thread
123116
onMessage({
124117
data: {
@@ -184,12 +177,10 @@ function checkQuery(q: string) {
184177
post({ type: "favoriteTools" });
185178
} else {
186179
post({
187-
type: "searchToolsByKeys",
180+
type: "searchTools",
188181
payload: {
189182
tools: props.toolsList,
190-
keys: KEYS,
191183
query: q,
192-
panelView: props.currentPanelView,
193184
currentPanel: props.currentPanel,
194185
},
195186
});

client/src/components/Panels/ToolBox.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import toolsList from "components/ToolsView/testData/toolsList";
44
import toolsListInPanel from "components/ToolsView/testData/toolsListInPanel";
55
import { useConfig } from "composables/config";
66

7-
import { createSortedResultObject, filterTools } from "./utilities";
7+
import { createSortedResultPanel, filterTools } from "./utilities";
88

99
jest.mock("composables/config");
1010
useConfig.mockReturnValue({
@@ -41,7 +41,7 @@ describe("ToolBox", () => {
4141
const matchedTools = resultIds.map((id) => {
4242
return { id: id, sections: [], order: 0 };
4343
});
44-
const toolsResultsSection = createSortedResultObject(matchedTools, toolPanelMock);
44+
const toolsResultsSection = createSortedResultPanel(matchedTools, toolPanelMock);
4545
expect(toolsResultsSection.idResults).toEqual(resultIds);
4646
const resultSectionIds = Object.keys(toolsResultsSection.resultPanel);
4747
expect(resultSectionIds.length).toBe(2);

client/src/components/Panels/toolSearch.worker.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
* A Web Worker that isolates the Tool Search into its own thread
33
*/
44

5-
import { searchToolsByKeys } from "./utilities";
5+
import { searchTools } from "./utilities";
66

77
// listen for messages from the main thread
88
self.addEventListener("message", (event) => {
99
const { type, payload } = event.data;
10-
if (type === "searchToolsByKeys") {
11-
const { tools, keys, query, panelView, currentPanel } = payload;
12-
const { results, resultPanel, closestTerm } = searchToolsByKeys(tools, keys, query, panelView, currentPanel);
10+
if (type === "searchTools") {
11+
const { tools, query, currentPanel } = payload;
12+
const { results, resultPanel, closestTerm } = searchTools(tools, query, currentPanel);
1313
// send the result back to the main thread
1414
self.postMessage({
1515
type: "searchToolsByKeysResult",

client/src/components/Panels/utilities.test.js renamed to client/src/components/Panels/utilities.test.ts

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import toolsList from "components/ToolsView/testData/toolsList";
2-
import toolsListInPanel from "components/ToolsView/testData/toolsListInPanel";
1+
// eslint-disable-next-line simple-import-sort/imports
2+
import toolsListUntyped from "@/components/ToolsView/testData/toolsList.json";
3+
import toolsListInPanelUntyped from "@/components/ToolsView/testData/toolsListInPanel.json";
34

45
import {
5-
createSortedResultObject,
6+
createSortedResultPanel,
67
createWhooshQuery,
78
determineWidth,
89
filterTools,
9-
searchToolsByKeys,
10+
searchObjectsByKeys,
11+
type SearchCommonKeys,
1012
} from "./utilities";
13+
import type { Tool, ToolSection } from "@/stores/toolStore";
1114

1215
describe("test helpers in tool searching utilities and panel handling", () => {
1316
it("panel width determination", () => {
@@ -18,6 +21,9 @@ describe("test helpers in tool searching utilities and panel handling", () => {
1821
});
1922
});
2023

24+
const toolsList = toolsListUntyped as unknown as Tool[];
25+
const toolsListInPanel = toolsListInPanelUntyped as unknown as Record<string, Tool | ToolSection>;
26+
2127
const tempToolPanel = {
2228
default: {
2329
"fasta/fastq": {
@@ -45,18 +51,35 @@ const tempToolsList = {
4551
id: "umi_tools_reduplicate",
4652
name: "UMI-tools reduplicate",
4753
},
48-
},
54+
} as unknown as Record<string, Tool>,
4955
};
5056

5157
describe("test helpers in tool searching utilities", () => {
58+
// Intentionally did not import the `searchTools` function from the util file
59+
// to be able to test different key sort orders here.
60+
function searchToolsByKeys(
61+
tools: Tool[],
62+
keys: SearchCommonKeys,
63+
query: string,
64+
currentPanel: Record<string, Tool | ToolSection>,
65+
): {
66+
results: string[];
67+
resultPanel: Record<string, Tool | ToolSection>;
68+
closestTerm: string | null;
69+
} {
70+
const { matchedResults, closestTerm } = searchObjectsByKeys<Tool>(tools, keys, query, ["name", "description"]);
71+
const { idResults, resultPanel } = createSortedResultPanel(matchedResults, currentPanel);
72+
return { results: idResults, resultPanel: resultPanel, closestTerm: closestTerm };
73+
}
74+
5275
it("test parsing helper that converts settings to whoosh query", async () => {
5376
const settings = {
5477
name: "Filter",
5578
id: "__FILTER_FAILED_DATASETS__",
5679
help: "downstream",
5780
owner: "devteam",
5881
};
59-
const q = createWhooshQuery(settings, "default", []);
82+
const q = createWhooshQuery(settings);
6083

6184
// OrGroup (at backend) on name, name_exact, description
6285
expect(q).toContain("name:(Filter) name_exact:(Filter) description:(Filter)");
@@ -69,7 +92,13 @@ describe("test helpers in tool searching utilities", () => {
6992
});
7093

7194
it("test tool search helper that searches for tools given keys", async () => {
72-
const searches = [
95+
const searches: {
96+
q: string;
97+
expectedResults: string[];
98+
keys: SearchCommonKeys;
99+
tools: Tool[];
100+
panel: Record<string, Tool | ToolSection>;
101+
}[] = [
73102
{
74103
// description prioritized
75104
q: "collection",
@@ -165,7 +194,7 @@ describe("test helpers in tool searching utilities", () => {
165194
},
166195
];
167196
searches.forEach((search) => {
168-
const { results } = searchToolsByKeys(search.tools, search.keys, search.q, "default", search.panel);
197+
const { results } = searchToolsByKeys(search.tools, search.keys, search.q, search.panel);
169198
expect(results).toEqual(search.expectedResults);
170199
});
171200
});
@@ -176,37 +205,38 @@ describe("test helpers in tool searching utilities", () => {
176205
// Testing if just names work with DL search
177206
const filterQueries = ["Fillter", "FILYER", " Fitler", " filtr"];
178207
filterQueries.forEach((q) => {
179-
const { results, closestTerm } = searchToolsByKeys(toolsList, keys, q, "default", toolsListInPanel);
208+
const { results, closestTerm } = searchToolsByKeys(toolsList, keys, q, toolsListInPanel);
180209
expect(results).toEqual(expectedResults);
181210
expect(closestTerm).toEqual("filter");
182211
});
183212
// Testing if names and description function with DL search
184213
let queries = ["datases from a collection", "from a colleection", "from a colleection"];
185214
queries.forEach((q) => {
186-
const { results } = searchToolsByKeys(toolsList, keys, q, "default", toolsListInPanel);
215+
const { results } = searchToolsByKeys(toolsList, keys, q, toolsListInPanel);
187216
expect(results).toEqual(expectedResults);
188217
});
189218
// Testing if different length queries correctly trigger changes in max DL distance
190219
queries = ["datae", "ppasetsfrom", "datass from a cppollection"];
191220
queries.forEach((q) => {
192-
const { results } = searchToolsByKeys(toolsList, keys, q, "default", toolsListInPanel);
221+
const { results } = searchToolsByKeys(toolsList, keys, q, toolsListInPanel);
193222
expect(results).toEqual(expectedResults);
194223
});
195224
});
196225

197226
it("test tool filtering helpers on toolsList given list of ids", async () => {
198227
const ids = ["__FILTER_FAILED_DATASETS__", "liftOver1"];
199228
// check length of first section from imported const toolsList
200-
expect(toolsListInPanel["collection_operations"].tools).toHaveLength(4);
229+
const collectionOperationsSection = toolsListInPanel["collection_operations"] as ToolSection;
230+
expect(collectionOperationsSection.tools).toHaveLength(4);
201231
// check length of same section from filtered toolsList
202232
const matchedTools = ids.map((id) => {
203233
return { id: id, sections: [], order: 0 };
204234
});
205-
const toolResultsPanel = createSortedResultObject(matchedTools, toolsListInPanel);
206-
const toolResultsSection = toolResultsPanel.resultPanel["collection_operations"];
235+
const toolResultsPanel = createSortedResultPanel(matchedTools, toolsListInPanel);
236+
const toolResultsSection = toolResultsPanel.resultPanel["collection_operations"] as ToolSection;
207237
expect(toolResultsSection.tools).toHaveLength(1);
208238
// check length of filtered tools (regardless of sections)
209-
const toolsById = toolsList.reduce((acc, item) => {
239+
const toolsById = toolsList.reduce<Record<string, Tool>>((acc, item) => {
210240
acc[item.id] = item;
211241
return acc;
212242
}, {});

0 commit comments

Comments
 (0)