Skip to content

Commit

Permalink
add multiple (optional) filters to client tool search
Browse files Browse the repository at this point in the history
This allows users to filter on tool ids or sections if they specify `id:tool_id` or `section:Section name`. Fixes galaxyproject#17741
  • Loading branch information
ahmedhamidawan committed Apr 19, 2024
1 parent a5d6688 commit 30ccce9
Showing 1 changed file with 34 additions and 12 deletions.
46 changes: 34 additions & 12 deletions client/src/components/Panels/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -46,6 +49,7 @@ export interface ToolSearchKeys {

interface SearchMatch {
id: string;
/** The order of the match, higher number = higher rank in results */
order: number;
}

Expand Down Expand Up @@ -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(" ");
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<string, string[]>) {
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();
}
}
Expand Down

0 comments on commit 30ccce9

Please sign in to comment.