Skip to content

Commit

Permalink
Add multiword type handling in global search
Browse files Browse the repository at this point in the history
  • Loading branch information
kowalczyk-krzysztof committed Oct 14, 2024
1 parent 62ca320 commit 3cf06fa
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export const SearchBar: FC<SearchBarProps> = (opts) => {
reportEvent.searchRequest();
}

const rawParams = parseSearchParams(searchValue.toLowerCase());
const rawParams = parseSearchParams(searchValue.toLowerCase(), searchableTypes);
let tagIds: string[] | undefined;
if (taggingApi && rawParams.filters.tags) {
tagIds = rawParams.filters.tags.map(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,33 @@ import { parseSearchParams } from './parse_search_params';

describe('parseSearchParams', () => {
it('returns the correct term', () => {
const searchParams = parseSearchParams('tag:(my-tag OR other-tag) hello');
const searchParams = parseSearchParams('tag:(my-tag OR other-tag) hello', []);
expect(searchParams.term).toEqual('hello');
});

it('returns the raw query as `term` in case of parsing error', () => {
const searchParams = parseSearchParams('tag:((()^invalid');
const searchParams = parseSearchParams('tag:((()^invalid', []);
expect(searchParams).toEqual({
term: 'tag:((()^invalid',
filters: {},
});
});

it('returns `undefined` term if query only contains field clauses', () => {
const searchParams = parseSearchParams('tag:(my-tag OR other-tag)');
const searchParams = parseSearchParams('tag:(my-tag OR other-tag)', []);
expect(searchParams.term).toBeUndefined();
});

it('returns correct filters when no field clause is defined', () => {
const searchParams = parseSearchParams('hello');
const searchParams = parseSearchParams('hello', []);
expect(searchParams.filters).toEqual({
tags: undefined,
types: undefined,
});
});

it('returns correct filters when field clauses are present', () => {
const searchParams = parseSearchParams('tag:foo type:bar hello tag:dolly');
const searchParams = parseSearchParams('tag:foo type:bar hello tag:dolly', []);
expect(searchParams).toEqual({
term: 'hello',
filters: {
Expand All @@ -46,7 +46,7 @@ describe('parseSearchParams', () => {
});

it('considers unknown field clauses to be part of the raw search term', () => {
const searchParams = parseSearchParams('tag:foo unknown:bar hello');
const searchParams = parseSearchParams('tag:foo unknown:bar hello', []);
expect(searchParams).toEqual({
term: 'unknown:bar hello',
filters: {
Expand All @@ -56,7 +56,7 @@ describe('parseSearchParams', () => {
});

it('handles aliases field clauses', () => {
const searchParams = parseSearchParams('tag:foo tags:bar type:dash types:board hello');
const searchParams = parseSearchParams('tag:foo tags:bar type:dash types:board hello', []);
expect(searchParams).toEqual({
term: 'hello',
filters: {
Expand All @@ -67,7 +67,7 @@ describe('parseSearchParams', () => {
});

it('converts boolean and number values to string for known filters', () => {
const searchParams = parseSearchParams('tag:42 tags:true type:69 types:false hello');
const searchParams = parseSearchParams('tag:42 tags:true type:69 types:false hello', []);
expect(searchParams).toEqual({
term: 'hello',
filters: {
Expand All @@ -76,4 +76,18 @@ describe('parseSearchParams', () => {
},
});
});

it('converts multiword searchable types to phrases so they get picked up as types', () => {
const mockSearchableMultiwordTypes = ['canvas-workpad', 'enterprise search'];
const searchParams = parseSearchParams(
'type:canvas workpad types:canvas-workpad hello type:enterprise search type:not multiword',
mockSearchableMultiwordTypes
);
expect(searchParams).toEqual({
term: 'hello multiword',
filters: {
types: ['canvas workpad', 'enterprise search', 'not', 'canvas-workpad'],
},
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,40 @@ const aliasMap = {
type: ['types'],
};

export const parseSearchParams = (term: string): ParsedSearchParams => {
// Converts multi word types to phrases by wrapping them in quotes. Example: type:canvas workpad -> type:"canvas workpad"
const convertMultiwordTypesToPhrases = (term: string, multiWordTypes: string[]): string => {
if (multiWordTypes.length === 0) {
return term;
}

const typesPattern = multiWordTypes.join('|');
const canvasWorkpadRegex = new RegExp(`(type:|types:)(\\s*[^"']*?)\\b(${typesPattern})\\b`, 'gi');

const modifiedTerm = term.replace(
canvasWorkpadRegex,
(match, typePrefix, additionalTextInBetween, matchedPhrase) =>
`${typePrefix}${additionalTextInBetween}"${matchedPhrase}"`
);

return modifiedTerm;
};

export const parseSearchParams = (term: string, searchableTypes: string[]): ParsedSearchParams => {
const recognizedFields = knownFilters.concat(...Object.values(aliasMap));
let query: Query;

// Finds all multiword types that are separated by whitespace or hyphens
const multiWordSearchableTypesWhitespaceSeperated = searchableTypes
.filter((item) => /[ -]/.test(item))
.map((item) => item.replace(/-/g, ' '));

const modifiedTerm = convertMultiwordTypesToPhrases(
term,
multiWordSearchableTypesWhitespaceSeperated
);

try {
query = Query.parse(term, {
query = Query.parse(modifiedTerm, {
schema: { recognizedFields },
});
} catch (e) {
Expand Down

0 comments on commit 3cf06fa

Please sign in to comment.