[ARVADOS-WORKBENCH2] updated: 1.3.1-408-gff9d56a3

Git user git at public.curoverse.com
Wed Mar 13 07:19:11 EDT 2019


Summary of changes:
 src/store/search-bar/search-bar-actions.test.ts    |  82 +------
 src/store/search-bar/search-bar-actions.ts         | 242 +++------------------
 src/store/search-bar/search-query/arv-parser.ts    |  74 +++++++
 src/store/search-bar/search-query/parser.ts        |  42 ++++
 .../search-results-middleware-service.ts           |  19 +-
 5 files changed, 162 insertions(+), 297 deletions(-)
 create mode 100644 src/store/search-bar/search-query/arv-parser.ts
 create mode 100644 src/store/search-bar/search-query/parser.ts

       via  ff9d56a31288124381f58f6577a490c0d6867bc3 (commit)
       via  b98cbbde17c336a78e2ab37bd68e85d1818d5155 (commit)
       via  d40a26653d3b3c45f3a703b44e860eb01a14c794 (commit)
       via  434449c7360d62eb867ad85b63da185e8a2681df (commit)
       via  635d694d0dde990f52c2e871f5e490cc6e3daefa (commit)
       via  bf5bf5faa1f61a2b78ff6153daff70a7bb08e939 (commit)
       via  fd539eab6dd2e86736a0d4f6aa97b4249df3ed1f (commit)
       via  c6705c2bbb9318719c58d5830acd8c0903115be9 (commit)
      from  a5cc3b71b39f304f15c03b175e541b5adf4541f7 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit ff9d56a31288124381f58f6577a490c0d6867bc3
Merge: a5cc3b71 b98cbbde
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Wed Mar 13 12:18:54 2019 +0100

    Merge branch '14917-searching-by-properties'
    
    refs #14917
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>


commit b98cbbde17c336a78e2ab37bd68e85d1818d5155
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Wed Mar 13 12:16:27 2019 +0100

    Use fullTextSearch filter only if search query is not empty
    
    Feature #14917
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/store/search-bar/search-bar-actions.ts b/src/store/search-bar/search-bar-actions.ts
index 3a6973f5..a513a6bb 100644
--- a/src/store/search-bar/search-bar-actions.ts
+++ b/src/store/search-bar/search-bar-actions.ts
@@ -327,6 +327,10 @@ export const queryToFilters = (query: string) => {
     const filter = new FilterBuilder();
     const resourceKind = data.type;
 
+    if(data.searchValue){
+        filter.addFullTextSearch(data.searchValue);
+    }
+
     if (data.projectUuid) {
         filter.addEqual('ownerUuid', data.projectUuid);
     }
@@ -350,7 +354,6 @@ export const queryToFilters = (query: string) => {
 
     return filter
         .addIsA("uuid", buildUuidFilter(resourceKind))
-        .addFullTextSearch(data.searchValue)
         .getFilters();
 };
 

commit d40a26653d3b3c45f3a703b44e860eb01a14c794
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Wed Mar 13 12:08:34 2019 +0100

    Update search-results to use new query parser
    
    Feature #14917
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/store/search-bar/search-bar-actions.test.ts b/src/store/search-bar/search-bar-actions.test.ts
index ea290b4d..51a73cc3 100644
--- a/src/store/search-bar/search-bar-actions.test.ts
+++ b/src/store/search-bar/search-bar-actions.test.ts
@@ -2,87 +2,23 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { getAdvancedDataFromQuery, getQueryFromAdvancedData, parseSearchQuery } from "~/store/search-bar/search-bar-actions";
+import { getAdvancedDataFromQuery, getQueryFromAdvancedData } from "~/store/search-bar/search-bar-actions";
 import { ResourceKind } from "~/models/resource";
 
 describe('search-bar-actions', () => {
-    describe('parseSearchQuery', () => {
-        it('should correctly parse query #1', () => {
-            const q = 'val0 is:trashed val1';
-            const r = parseSearchQuery(q);
-            expect(r.hasKeywords).toBeTruthy();
-            expect(r.values).toEqual(['val0', 'val1']);
-            expect(r.properties).toEqual({
-                is: ['trashed']
-            });
-        });
-
-        it('should correctly parse query #2 (value with keyword should be ignored)', () => {
-            const q = 'val0 is:from:trashed val1';
-            const r = parseSearchQuery(q);
-            expect(r.hasKeywords).toBeTruthy();
-            expect(r.values).toEqual(['val0', 'val1']);
-            expect(r.properties).toEqual({
-                from: ['trashed']
-            });
-        });
-
-        it('should correctly parse query #3 (many keywords)', () => {
-            const q = 'val0 is:trashed val2 from:2017-04-01 val1';
-            const r = parseSearchQuery(q);
-            expect(r.hasKeywords).toBeTruthy();
-            expect(r.values).toEqual(['val0', 'val2', 'val1']);
-            expect(r.properties).toEqual({
-                is: ['trashed'],
-                from: ['2017-04-01']
-            });
-        });
-
-        it('should correctly parse query #4 (no duplicated values)', () => {
-            const q = 'val0 is:trashed val2 val2 val0';
-            const r = parseSearchQuery(q);
-            expect(r.hasKeywords).toBeTruthy();
-            expect(r.values).toEqual(['val0', 'val2']);
-            expect(r.properties).toEqual({
-                is: ['trashed']
-            });
-        });
-
-        it('should correctly parse query #5 (properties)', () => {
-            const q = 'val0 has:filesize:100mb val2 val2 val0';
-            const r = parseSearchQuery(q);
-            expect(r.hasKeywords).toBeTruthy();
-            expect(r.values).toEqual(['val0', 'val2']);
-            expect(r.properties).toEqual({
-                'has': ['filesize:100mb']
-            });
-        });
-
-        it('should correctly parse query #6 (multiple properties, multiple is)', () => {
-            const q = 'val0 has:filesize:100mb val2 has:user:daniel is:starred val2 val0 is:trashed';
-            const r = parseSearchQuery(q);
-            expect(r.hasKeywords).toBeTruthy();
-            expect(r.values).toEqual(['val0', 'val2']);
-            expect(r.properties).toEqual({
-                'has': ['filesize:100mb', 'user:daniel'],
-                'is': ['starred', 'trashed']
-            });
-        });
-    });
-
     describe('getAdvancedDataFromQuery', () => {
         it('should correctly build advanced data record from query #1', () => {
-            const r = getAdvancedDataFromQuery('val0 has:filesize:100mb val2 has:user:daniel is:starred val2 val0 is:trashed');
+            const r = getAdvancedDataFromQuery('val0 has:"file size":"100mb" val2 has:"user":"daniel" is:starred val2 val0 is:trashed');
             expect(r).toEqual({
                 searchValue: 'val0 val2',
                 type: undefined,
                 cluster: undefined,
                 projectUuid: undefined,
                 inTrash: true,
-                dateFrom: undefined,
-                dateTo: undefined,
+                dateFrom: '',
+                dateTo: '',
                 properties: [{
-                    key: 'filesize',
+                    key: 'file size',
                     value: '100mb'
                 }, {
                     key: 'user',
@@ -94,7 +30,7 @@ describe('search-bar-actions', () => {
         });
 
         it('should correctly build advanced data record from query #2', () => {
-            const r = getAdvancedDataFromQuery('document from:2017-08-01 pdf has:filesize:101mb is:trashed type:arvados#collection cluster:c97qx');
+            const r = getAdvancedDataFromQuery('document from:2017-08-01 pdf has:"filesize":"101mb" is:trashed type:arvados#collection cluster:c97qx');
             expect(r).toEqual({
                 searchValue: 'document pdf',
                 type: ResourceKind.COLLECTION,
@@ -102,7 +38,7 @@ describe('search-bar-actions', () => {
                 projectUuid: undefined,
                 inTrash: true,
                 dateFrom: '2017-08-01',
-                dateTo: undefined,
+                dateTo: '',
                 properties: [{
                     key: 'filesize',
                     value: '101mb'
@@ -124,13 +60,13 @@ describe('search-bar-actions', () => {
                 dateFrom: '2017-08-01',
                 dateTo: '',
                 properties: [{
-                    key: 'filesize',
+                    key: 'file size',
                     value: '101mb'
                 }],
                 saveQuery: false,
                 queryName: ''
             });
-            expect(q).toBe('document pdf type:arvados#collection cluster:c97qx is:trashed from:2017-08-01 has:filesize:101mb');
+            expect(q).toBe('document pdf type:arvados#collection cluster:c97qx is:trashed from:2017-08-01 has:"file size":"101mb"');
         });
     });
 });
diff --git a/src/store/search-bar/search-bar-actions.ts b/src/store/search-bar/search-bar-actions.ts
index d6aae926..3a6973f5 100644
--- a/src/store/search-bar/search-bar-actions.ts
+++ b/src/store/search-bar/search-bar-actions.ts
@@ -22,6 +22,8 @@ import { activateSearchBarProject } from "~/store/search-bar/search-bar-tree-act
 import { Session } from "~/models/session";
 import { searchResultsPanelActions } from "~/store/search-results-panel/search-results-panel-actions";
 import { ListResults } from "~/services/common-service/common-service";
+import * as parser from './search-query/arv-parser';
+import { Keywords } from './search-query/arv-parser';
 
 export const searchBarActions = unionize({
     SET_CURRENT_VIEW: ofType<string>(),
@@ -210,11 +212,10 @@ const searchGroups = (searchValue: string, limit: number) =>
         const currentView = getState().searchBar.currentView;
 
         if (searchValue || currentView === SearchView.ADVANCED) {
-            const sq = parseSearchQuery(searchValue);
-            const clusterId = getSearchQueryFirstProp(sq, 'cluster');
+            const { cluster: clusterId } = getAdvancedDataFromQuery(searchValue);
             const sessions = getSearchSessions(clusterId, getState().auth.sessions);
             const lists: ListResults<GroupContentsResource>[] = await Promise.all(sessions.map(session => {
-                const filters = searchQueryToFilters(sq);
+                const filters = queryToFilters(searchValue);
                 return services.groupsService.contents('', {
                     filters,
                     limit,
@@ -279,7 +280,7 @@ export const getQueryFromAdvancedData = (data: SearchBarAdvanceFormData, prevDat
         ['type', 'type'],
         ['cluster', 'cluster'],
         ['project', 'projectUuid'],
-        ['is:trashed', 'inTrash'],
+        [`is:${parser.States.TRASHED}`, 'inTrash'],
         ['from', 'dateFrom'],
         ['to', 'dateTo']
     ];
@@ -300,98 +301,18 @@ export const getQueryFromAdvancedData = (data: SearchBarAdvanceFormData, prevDat
     return value;
 };
 
-export class ParseSearchQuery {
-    hasKeywords: boolean;
-    values: string[];
-    properties: {
-        [key: string]: string[]
-    };
-}
-
-export const parseSearchQuery: (query: string) => ParseSearchQuery = (searchValue: string) => {
-    const keywords = [
-        'type:',
-        'cluster:',
-        'project:',
-        'is:',
-        'from:',
-        'to:',
-        'has:'
-    ];
-
-    const hasKeywords = (search: string) => keywords.reduce((acc, keyword) => acc + (search.includes(keyword) ? 1 : 0), 0);
-    let keywordsCnt = 0;
-
-    const properties = {};
-
-    keywords.forEach(k => {
-        if (k) {
-            let p = searchValue.indexOf(k);
-            const key = k.substr(0, k.length - 1);
-
-            while (p >= 0) {
-                const l = searchValue.length;
-                keywordsCnt += 1;
-
-                let v = '';
-                let i = p + k.length;
-                while (i < l && searchValue[i] === ' ') {
-                    ++i;
-                }
-                const vp = i;
-                while (i < l && searchValue[i] !== ' ') {
-                    v += searchValue[i];
-                    ++i;
-                }
-
-                if (hasKeywords(v)) {
-                    searchValue = searchValue.substr(0, p) + searchValue.substr(vp);
-                } else {
-                    if (v !== '') {
-                        if (!properties[key]) {
-                            properties[key] = [];
-                        }
-                        properties[key].push(v);
-                    }
-                    searchValue = searchValue.substr(0, p) + searchValue.substr(i);
-                }
-                p = searchValue.indexOf(k);
-            }
-        }
-    });
-
-    const values = _.uniq(searchValue.split(' ').filter(v => v.length > 0));
-
-    return { hasKeywords: keywordsCnt > 0, values, properties };
-};
-
-export const getSearchQueryFirstProp = (sq: ParseSearchQuery, name: string) => sq.properties[name] && sq.properties[name][0];
-export const getSearchQueryPropValue = (sq: ParseSearchQuery, name: string, value: string) => sq.properties[name] && sq.properties[name].find((v: string) => v === value);
-export const getSearchQueryProperties = (sq: ParseSearchQuery): PropertyValue[] => {
-    if (sq.properties.has) {
-        return sq.properties.has.map((value: string) => {
-            const v = value.split(':');
-            return {
-                key: v[0],
-                value: v[1]
-            };
-        });
-    }
-    return [];
-};
-
 export const getAdvancedDataFromQuery = (query: string): SearchBarAdvanceFormData => {
-    const sq = parseSearchQuery(query);
-
+    const { tokens, searchString } = parser.parseSearchQuery(query);
+    const getValue = parser.getValue(tokens);
     return {
-        searchValue: sq.values.join(' '),
-        type: getSearchQueryFirstProp(sq, 'type') as ResourceKind,
-        cluster: getSearchQueryFirstProp(sq, 'cluster'),
-        projectUuid: getSearchQueryFirstProp(sq, 'project'),
-        inTrash: getSearchQueryPropValue(sq, 'is', 'trashed') !== undefined,
-        dateFrom: getSearchQueryFirstProp(sq, 'from'),
-        dateTo: getSearchQueryFirstProp(sq, 'to'),
-        properties: getSearchQueryProperties(sq),
+        searchValue: searchString,
+        type: getValue(Keywords.TYPE) as ResourceKind,
+        cluster: getValue(Keywords.CLUSTER),
+        projectUuid: getValue(Keywords.PROJECT),
+        inTrash: parser.isTrashed(tokens),
+        dateFrom: getValue(Keywords.FROM) || '',
+        dateTo: getValue(Keywords.TO) || '',
+        properties: parser.getProperties(tokens),
         saveQuery: false,
         queryName: ''
     };
@@ -401,27 +322,24 @@ export const getSearchSessions = (clusterId: string | undefined, sessions: Sessi
     return sessions.filter(s => s.loggedIn && (!clusterId || s.clusterId === clusterId));
 };
 
-export const searchQueryToFilters = (sq: ParseSearchQuery): string => {
+export const queryToFilters = (query: string) => {
+    const data = getAdvancedDataFromQuery(query);
     const filter = new FilterBuilder();
-    const resourceKind = getSearchQueryFirstProp(sq, 'type') as ResourceKind;
+    const resourceKind = data.type;
 
-    const projectUuid = getSearchQueryFirstProp(sq, 'project');
-    if (projectUuid) {
-        filter.addEqual('ownerUuid', projectUuid);
+    if (data.projectUuid) {
+        filter.addEqual('ownerUuid', data.projectUuid);
     }
 
-    const dateFrom = getSearchQueryFirstProp(sq, 'from');
-    if (dateFrom) {
-        filter.addGte('modified_at', buildDateFilter(dateFrom));
+    if (data.dateFrom) {
+        filter.addGte('modified_at', buildDateFilter(data.dateFrom));
     }
 
-    const dateTo = getSearchQueryFirstProp(sq, 'to');
-    if (dateTo) {
-        filter.addLte('modified_at', buildDateFilter(dateTo));
+    if (data.dateTo) {
+        filter.addLte('modified_at', buildDateFilter(data.dateTo));
     }
 
-    const props = getSearchQueryProperties(sq);
-    props.forEach(p => {
+    data.properties.forEach(p => {
         if (p.value) {
             filter
                 .addILike(`properties.${p.key}`, p.value, GroupContentsResourcePrefix.PROJECT)
@@ -432,7 +350,7 @@ export const searchQueryToFilters = (sq: ParseSearchQuery): string => {
 
     return filter
         .addIsA("uuid", buildUuidFilter(resourceKind))
-        .addFullTextSearch(sq.values.join(' '))
+        .addFullTextSearch(data.searchValue)
         .getFilters();
 };
 
diff --git a/src/store/search-results-panel/search-results-middleware-service.ts b/src/store/search-results-panel/search-results-middleware-service.ts
index a855dc46..9d7e3207 100644
--- a/src/store/search-results-panel/search-results-middleware-service.ts
+++ b/src/store/search-results-panel/search-results-middleware-service.ts
@@ -16,11 +16,9 @@ import { GroupContentsResource, GroupContentsResourcePrefix } from "~/services/g
 import { ListResults } from '~/services/common-service/common-service';
 import { searchResultsPanelActions } from '~/store/search-results-panel/search-results-panel-actions';
 import {
-    getSearchQueryFirstProp,
-    getSearchSessions, ParseSearchQuery,
-    parseSearchQuery,
-    searchQueryToFilters,
-    getSearchQueryPropValue
+    getSearchSessions,
+    queryToFilters,
+    getAdvancedDataFromQuery
 } from '~/store/search-bar/search-bar-actions';
 import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer";
 import { joinFilters } from '~/services/api/filter-builder';
@@ -39,8 +37,7 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic
         const state = api.getState();
         const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
         const searchValue = state.searchBar.searchValue;
-        const sq = parseSearchQuery(searchValue);
-        const clusterId = getSearchQueryFirstProp(sq, 'cluster');
+        const { cluster: clusterId } = getAdvancedDataFromQuery(searchValue);
         const sessions = getSearchSessions(clusterId, state.auth.sessions);
 
         if (searchValue.trim() === '') {
@@ -48,7 +45,7 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic
         }
 
         try {
-            const params = getParams(dataExplorer, sq);
+            const params = getParams(dataExplorer, searchValue);
 
             const responses = await Promise.all(sessions.map(session =>
                 this.services.groupsService.contents('', params, session)
@@ -82,14 +79,14 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic
 
 const typeFilters = (columns: DataColumns<string>) => serializeResourceTypeFilters(getDataExplorerColumnFilters(columns, ProjectPanelColumnNames.TYPE));
 
-export const getParams = (dataExplorer: DataExplorer, sq: ParseSearchQuery) => ({
+export const getParams = (dataExplorer: DataExplorer, query: string) => ({
     ...dataExplorerToListParams(dataExplorer),
     filters: joinFilters(
-        searchQueryToFilters(sq),
+        queryToFilters(query),
         typeFilters(dataExplorer.columns)
     ),
     order: getOrder(dataExplorer),
-    includeTrash: (!!getSearchQueryPropValue(sq, 'is', 'trashed')) || false
+    includeTrash: getAdvancedDataFromQuery(query).inTrash
 });
 
 const getOrder = (dataExplorer: DataExplorer) => {

commit 434449c7360d62eb867ad85b63da185e8a2681df
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Wed Mar 13 12:06:40 2019 +0100

    Add isTrashed function to arv-parser
    
    Feature #14917
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/store/search-bar/search-query/arv-parser.ts b/src/store/search-bar/search-query/arv-parser.ts
index 3a52be6c..c9b7024b 100644
--- a/src/store/search-bar/search-query/arv-parser.ts
+++ b/src/store/search-bar/search-query/arv-parser.ts
@@ -19,6 +19,10 @@ export enum Keywords {
     TO = 'to',
 }
 
+export enum States {
+    TRASHED = 'trashed'
+}
+
 const keyValuePattern = (key: string) => new RegExp(`${key}:([^ ]*)`);
 const propertyPattern = /has:"(.*?)":"(.*?)"/;
 
@@ -54,3 +58,17 @@ export const getProperties = (tokens: string[]) =>
         }
         return properties;
     }, [] as Property[]);
+
+
+export const isTrashed = (tokens: string[]) => {
+    for (const token of tokens) {
+        const match = token.match(keyValuePattern(Keywords.IS)) || ['', ''];
+        if (match) {
+            const [, value] = match;
+            if(value === States.TRASHED) {
+                return true;
+            }
+        }
+    }
+    return false;
+};

commit 635d694d0dde990f52c2e871f5e490cc6e3daefa
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Wed Mar 13 12:05:38 2019 +0100

    Remove duplicates from result of findSearchString
    
    Feature #14917
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/store/search-bar/search-query/parser.ts b/src/store/search-bar/search-query/parser.ts
index 5fb82e4d..6eb01b85 100644
--- a/src/store/search-bar/search-query/parser.ts
+++ b/src/store/search-bar/search-query/parser.ts
@@ -2,6 +2,8 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
+import { uniq } from 'lodash/fp';
+
 export interface ParsedSearchQuery {
     tokens: string[];
     searchString: string;
@@ -25,7 +27,12 @@ export const findAllTokens = (query: string, patterns: RegExp[]): string[] => {
 };
 
 export const findSearchString = (query: string, tokens: string[]) => {
-    return tokens.reduce((q, token) => q.replace(token, ''), query);
+    const uniqueWords = uniq(tokens
+        .reduce((q, token) => q.replace(token, ''), query)
+        .split(' ')
+        .filter(word => word !== '')
+    );
+    return uniqueWords.join(' ');
 };
 
 export const parseSearchQuery = (patterns: RegExp[]) => (query: string): ParsedSearchQuery => {

commit bf5bf5faa1f61a2b78ff6153daff70a7bb08e939
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Wed Mar 13 11:15:47 2019 +0100

    Create new search query parser
    
    Feature #14917
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/store/search-bar/search-query/arv-parser.ts b/src/store/search-bar/search-query/arv-parser.ts
new file mode 100644
index 00000000..3a52be6c
--- /dev/null
+++ b/src/store/search-bar/search-query/arv-parser.ts
@@ -0,0 +1,56 @@
+
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as parser from '~/store/search-bar/search-query/parser';
+
+interface Property {
+    key: string;
+    value: string;
+}
+
+export enum Keywords {
+    TYPE = 'type',
+    CLUSTER = 'cluster',
+    PROJECT = 'project',
+    IS = 'is',
+    FROM = 'from',
+    TO = 'to',
+}
+
+const keyValuePattern = (key: string) => new RegExp(`${key}:([^ ]*)`);
+const propertyPattern = /has:"(.*?)":"(.*?)"/;
+
+const patterns = [
+    keyValuePattern(Keywords.TYPE),
+    keyValuePattern(Keywords.CLUSTER),
+    keyValuePattern(Keywords.PROJECT),
+    keyValuePattern(Keywords.IS),
+    keyValuePattern(Keywords.FROM),
+    keyValuePattern(Keywords.TO),
+    propertyPattern
+];
+
+export const parseSearchQuery = parser.parseSearchQuery(patterns);
+
+export const getValue = (tokens: string[]) => (key: string) => {
+    const pattern = keyValuePattern(key);
+    const token = tokens.find(t => pattern.test(t));
+    if (token) {
+        const [, value] = token.split(':');
+        return value;
+    }
+    return undefined;
+};
+
+export const getProperties = (tokens: string[]) =>
+    tokens.reduce((properties, token) => {
+        const match = token.match(propertyPattern);
+        if (match) {
+            const [, key, value] = match;
+            const newProperty = { key, value };
+            return [...properties, newProperty];
+        }
+        return properties;
+    }, [] as Property[]);
diff --git a/src/store/search-bar/search-query/parser.ts b/src/store/search-bar/search-query/parser.ts
new file mode 100644
index 00000000..5fb82e4d
--- /dev/null
+++ b/src/store/search-bar/search-query/parser.ts
@@ -0,0 +1,35 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export interface ParsedSearchQuery {
+    tokens: string[];
+    searchString: string;
+}
+
+export const findToken = (query: string, patterns: RegExp[]) => {
+    for (const pattern of patterns) {
+        const match = query.match(pattern);
+        if (match) {
+            return match[0];
+        }
+    }
+    return null;
+};
+
+export const findAllTokens = (query: string, patterns: RegExp[]): string[] => {
+    const token = findToken(query, patterns);
+    return token
+        ? [token].concat(findAllTokens(query.replace(token, ''), patterns))
+        : [];
+};
+
+export const findSearchString = (query: string, tokens: string[]) => {
+    return tokens.reduce((q, token) => q.replace(token, ''), query);
+};
+
+export const parseSearchQuery = (patterns: RegExp[]) => (query: string): ParsedSearchQuery => {
+    const tokens = findAllTokens(query, patterns);
+    const searchString = findSearchString(query, tokens);
+    return { tokens, searchString };
+};

commit fd539eab6dd2e86736a0d4f6aa97b4249df3ed1f
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Tue Mar 12 17:59:57 2019 +0100

    Handle white space in property key and value by wprapping them with quotes
    
    Feature #14917
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/store/search-bar/search-bar-actions.ts b/src/store/search-bar/search-bar-actions.ts
index 1e47bdff..d6aae926 100644
--- a/src/store/search-bar/search-bar-actions.ts
+++ b/src/store/search-bar/search-bar-actions.ts
@@ -271,7 +271,7 @@ export const getQueryFromAdvancedData = (data: SearchBarAdvanceFormData, prevDat
             dateFrom: data.dateFrom,
             dateTo: data.dateTo,
         };
-        (data.properties || []).forEach(p => fo[`prop-${p.key}`] = p.value);
+        (data.properties || []).forEach(p => fo[`prop-"${p.key}"`] = `"${p.value}"`);
         return fo;
     };
 
@@ -284,7 +284,7 @@ export const getQueryFromAdvancedData = (data: SearchBarAdvanceFormData, prevDat
         ['to', 'dateTo']
     ];
     _.union(data.properties, prevData ? prevData.properties : [])
-        .forEach(p => keyMap.push([`has:${p.key}`, `prop-${p.key}`]));
+        .forEach(p => keyMap.push([`has:"${p.key}"`, `prop-"${p.key}"`]));
 
     if (prevData) {
         const obj = getModifiedKeysValues(flatData(data), flatData(prevData));

commit c6705c2bbb9318719c58d5830acd8c0903115be9
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Tue Mar 12 17:55:55 2019 +0100

    Delete unused getFilters function
    
    Feature #14917
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/store/search-bar/search-bar-actions.ts b/src/store/search-bar/search-bar-actions.ts
index b2b19a48..1e47bdff 100644
--- a/src/store/search-bar/search-bar-actions.ts
+++ b/src/store/search-bar/search-bar-actions.ts
@@ -401,111 +401,6 @@ export const getSearchSessions = (clusterId: string | undefined, sessions: Sessi
     return sessions.filter(s => s.loggedIn && (!clusterId || s.clusterId === clusterId));
 };
 
-export const getFilters = (filterName: string, searchValue: string, sq: ParseSearchQuery): string => {
-    const filter = new FilterBuilder();
-    const isSearchQueryUuid = isResourceUuid(sq.values[0]);
-    const resourceKind = getSearchQueryFirstProp(sq, 'type') as ResourceKind;
-
-    let prefix = '';
-    switch (resourceKind) {
-        case ResourceKind.COLLECTION:
-            prefix = GroupContentsResourcePrefix.COLLECTION;
-            break;
-        case ResourceKind.PROCESS:
-            prefix = GroupContentsResourcePrefix.PROCESS;
-            break;
-        default:
-            prefix = GroupContentsResourcePrefix.PROJECT;
-            break;
-    }
-
-    const isTrashed = getSearchQueryPropValue(sq, 'is', 'trashed');
-
-    if (!sq.hasKeywords && !isSearchQueryUuid) {
-        filter
-            .addILike(filterName, searchValue, GroupContentsResourcePrefix.COLLECTION)
-            .addILike(filterName, searchValue, GroupContentsResourcePrefix.PROJECT)
-            .addILike(filterName, searchValue, GroupContentsResourcePrefix.PROCESS)
-            .addEqual('is_trashed', false, GroupContentsResourcePrefix.COLLECTION)
-            .addEqual('is_trashed', false, GroupContentsResourcePrefix.PROJECT);
-
-        if (isTrashed) {
-            filter.addILike(filterName, searchValue, GroupContentsResourcePrefix.PROCESS);
-        }
-    } else if (!sq.hasKeywords && isSearchQueryUuid) {
-        filter
-            .addILike('uuid', searchValue, GroupContentsResourcePrefix.COLLECTION)
-            .addILike('uuid', searchValue, GroupContentsResourcePrefix.PROJECT)
-            .addILike('uuid', searchValue, GroupContentsResourcePrefix.PROCESS)
-            .addEqual('is_trashed', false, GroupContentsResourcePrefix.COLLECTION)
-            .addEqual('is_trashed', false, GroupContentsResourcePrefix.PROJECT);
-    }
-    else {
-        if (prefix) {
-            sq.values.forEach(v =>
-                filter.addILike(filterName, v, prefix)
-            );
-        } else {
-            sq.values.forEach(v => {
-                filter
-                    .addILike(filterName, v, GroupContentsResourcePrefix.COLLECTION)
-                    .addILike(filterName, v, GroupContentsResourcePrefix.PROJECT)
-                    .addILike(filterName, v, GroupContentsResourcePrefix.PROCESS)
-                    .addEqual('is_trashed', false, GroupContentsResourcePrefix.COLLECTION)
-                    .addEqual('is_trashed', false, GroupContentsResourcePrefix.PROJECT);
-
-                if (isTrashed) {
-                    filter.addILike(filterName, v, GroupContentsResourcePrefix.PROCESS);
-                }
-            });
-        }
-        if (prefix && !isTrashed) {
-            sq.values.forEach(v =>
-                filter.addILike(filterName, v, prefix)
-                    .addEqual('is_trashed', false, GroupContentsResourcePrefix.COLLECTION)
-                    .addEqual('is_trashed', false, GroupContentsResourcePrefix.PROJECT)
-            );
-        }
-
-        if (isTrashed) {
-            sq.values.forEach(v => {
-                filter.addEqual('is_trashed', true, GroupContentsResourcePrefix.COLLECTION)
-                    .addEqual('is_trashed', true, GroupContentsResourcePrefix.PROJECT)
-                    .addILike(filterName, v, GroupContentsResourcePrefix.COLLECTION)
-                    .addILike(filterName, searchValue, GroupContentsResourcePrefix.PROCESS);
-            });
-        }
-
-        const projectUuid = getSearchQueryFirstProp(sq, 'project');
-        if (projectUuid) {
-            filter.addEqual('uuid', projectUuid, prefix);
-        }
-
-        const dateFrom = getSearchQueryFirstProp(sq, 'from');
-        if (dateFrom) {
-            filter.addGte('modified_at', buildDateFilter(dateFrom));
-        }
-
-        const dateTo = getSearchQueryFirstProp(sq, 'to');
-        if (dateTo) {
-            filter.addLte('modified_at', buildDateFilter(dateTo));
-        }
-
-        const props = getSearchQueryProperties(sq);
-        props.forEach(p => {
-            if (p.value) {
-                filter.addILike(`properties.${p.key}`, p.value, GroupContentsResourcePrefix.PROJECT)
-                    .addILike(`properties.${p.key}`, p.value, GroupContentsResourcePrefix.COLLECTION);
-            }
-            filter.addExists(p.key);
-        });
-    }
-
-    return filter
-        .addIsA("uuid", buildUuidFilter(resourceKind))
-        .getFilters();
-};
-
 export const searchQueryToFilters = (sq: ParseSearchQuery): string => {
     const filter = new FilterBuilder();
     const resourceKind = getSearchQueryFirstProp(sq, 'type') as ResourceKind;

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list