From 30ccce9a9821c9b133bdaba296ffd44a4e0dd0b4 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Fri, 19 Apr 2024 17:01:47 -0500 Subject: [PATCH 1/9] add multiple (optional) filters to client tool search This allows users to filter on tool ids or sections if they specify `id:tool_id` or `section:Section name`. Fixes https://github.com/galaxyproject/galaxy/issues/17741 --- client/src/components/Panels/utilities.ts | 46 +++++++++++++++++------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/client/src/components/Panels/utilities.ts b/client/src/components/Panels/utilities.ts index a22b60fa1a02..95da1acbbf69 100644 --- a/client/src/components/Panels/utilities.ts +++ b/client/src/components/Panels/utilities.ts @@ -12,7 +12,10 @@ import { } from "@/stores/toolStore"; import levenshteinDistance from "@/utils/levenshtein"; -const TOOL_ID_KEYS = ["id", "tool_id"]; +const FILTER_KEYS = { + id: ["id", "tool_id"], + panel_section_name: ["section", "panel_section_name"], +}; const STRING_REPLACEMENTS: string[] = [" ", "-", "\\(", "\\)", "'", ":", `"`]; const MINIMUM_DL_LENGTH = 5; // for Demerau-Levenshtein distance const MINIMUM_WORD_MATCH = 2; // for word match @@ -46,6 +49,7 @@ export interface ToolSearchKeys { interface SearchMatch { id: string; + /** The order of the match, higher number = higher rank in results */ order: number; } @@ -264,11 +268,12 @@ export function searchToolsByKeys( } { const matchedTools: SearchMatch[] = []; let closestTerm = null; - // if user's query = "id:1234" or "tool_id:1234", only search for id - const id = processForId(query, TOOL_ID_KEYS); - if (id) { - query = id; - keys = { id: 1 }; + + // check if query is of the form "property:value" and then ONLY filter on that property + const { filteredQuery, filteredKeys } = filterOnKeys(query, FILTER_KEYS); + if (filteredQuery) { + query = filteredQuery; + keys = filteredKeys; } const queryWords = query.trim().toLowerCase().split(" "); @@ -338,7 +343,7 @@ export function searchToolsByKeys( } } // no results with string.match(): recursive call with usesDL - if (!id && !usesDL && matchedTools.length == 0) { + if (!filteredQuery && !usesDL && matchedTools.length == 0) { return searchToolsByKeys(tools, keys, query, panelView, currentPanel, true); } const { idResults, resultPanel } = createSortedResultObject(matchedTools, currentPanel); @@ -491,15 +496,32 @@ function sanitizeString(value: string, targets: string[] = [], substitute = "") } /** - * If the query is of the form "id:1234" (or "tool_id:1234"), return the id. + * If the query is of the form "property:value", return the value and keys which + * ONLY filter on that property. + * Otherwise, return null/empty object. + * @param query - the raw query + * @param keys - keys to filter for + */ +function filterOnKeys(query: string, keys: Record) { + for (const key in keys) { + const filteredQuery = processForProperty(query, keys[key] || []); + if (filteredQuery) { + return { filteredQuery, filteredKeys: { [key]: 1 } }; + } + } + return { filteredQuery: null, filteredKeys: {} }; +} + +/** + * If the query is of the form "property:value", return the value. * Otherwise, return null. * @param query - the raw query - * @param keys - Optional: keys to check for (default: ["id"]) - * @returns id or null + * @param keys - keys to check for + * @returns value or null */ -function processForId(query: string, keys = ["id"]) { +function processForProperty(query: string, keys: string[]) { for (const key of keys) { - if (query.includes(`${key}:`)) { + if (query.trim().startsWith(`${key}:`)) { return query.split(`${key}:`)[1]?.trim(); } } From 73bb46c610ef9873b7bf209264169e7912ae5ecb Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Mon, 22 Apr 2024 10:18:00 -0500 Subject: [PATCH 2/9] add jest for `section:...` filter in client tool search --- client/src/components/Panels/utilities.test.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/src/components/Panels/utilities.test.js b/client/src/components/Panels/utilities.test.js index d743e64c4d98..3d8e1b77e24b 100644 --- a/client/src/components/Panels/utilities.test.js +++ b/client/src/components/Panels/utilities.test.js @@ -147,6 +147,14 @@ describe("test helpers in tool searching utilities", () => { tools: Object.values(tempToolsList.tools), panel: tempToolPanel.default, }, + { + // section is searchable if provided "section:" + q: "section:Lift-Over", + expectedResults: ["liftOver1"], + keys: { description: 1, name: 2 }, + tools: toolsList, + panel: toolsListInPanel, + }, // if at least couple words match, return results { q: "filter datasets", From 4de84c8597c57a4b867c9ebd0e7a93f419562aa5 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Fri, 26 Apr 2024 22:01:18 -0500 Subject: [PATCH 3/9] refactor `ToolPanelLabel` to typescript & composition --- .../Panels/Common/ToolPanelLabel.vue | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/client/src/components/Panels/Common/ToolPanelLabel.vue b/client/src/components/Panels/Common/ToolPanelLabel.vue index 13dc23970c47..832713bdcaf0 100644 --- a/client/src/components/Panels/Common/ToolPanelLabel.vue +++ b/client/src/components/Panels/Common/ToolPanelLabel.vue @@ -1,29 +1,24 @@ + + - -