Merge branch '19051-handle-quotes-in-search' into main
[arvados-workbench2.git] / src / services / api / filter-builder.ts
index 77fcef6fca096bf8a13a406f1b3e0b1bd1a8ed25..da67935a1e5d8a44c5bf2c601c3fc639204ca8ab 100644 (file)
@@ -2,19 +2,21 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import * as _ from "lodash";
-
-export function joinFilters(filters0?: string, filters1?: string) {
-    return [filters0, filters1].filter(s => s).join(",");
+export function joinFilters(...filters: string[]) {
+    return filters.filter(s => s).join(",");
 }
 
 export class FilterBuilder {
     constructor(private filters = "") { }
 
-    public addEqual(field: string, value?: string | boolean | null, resourcePrefix?: string) {
+    public addEqual(field: string, value?: string | string[] | boolean | null, resourcePrefix?: string) {
         return this.addCondition(field, "=", value, "", "", resourcePrefix);
     }
 
+    public addDistinct(field: string, value?: string | boolean | null, resourcePrefix?: string) {
+        return this.addCondition(field, "!=", value, "", "", resourcePrefix);
+    }
+
     public addLike(field: string, value?: string, resourcePrefix?: string) {
         return this.addCondition(field, "like", value, "%", "%", resourcePrefix);
     }
@@ -23,6 +25,10 @@ export class FilterBuilder {
         return this.addCondition(field, "ilike", value, "%", "%", resourcePrefix);
     }
 
+    public addContains(field: string, value?: string, resourcePrefix?: string) {
+        return this.addCondition(field, "contains", value, "", "", resourcePrefix);
+    }
+
     public addIsA(field: string, value?: string | string[], resourcePrefix?: string) {
         return this.addCondition(field, "is_a", value, "", "", resourcePrefix);
     }
@@ -54,9 +60,23 @@ export class FilterBuilder {
     public addExists(value?: string, resourcePrefix?: string) {
         return this.addCondition("properties", "exists", value, "", "", resourcePrefix);
     }
+    public addDoesNotExist(field: string, resourcePrefix?: string) {
+        return this.addCondition("properties." + field, "exists", false, "", "", resourcePrefix);
+    }
 
     public addFullTextSearch(value: string) {
-        const terms = value.trim().split(/(\s+)/);
+        const regex = /"[^"]*"/;
+        const matches: any[] = [];
+
+        let match = value.match(regex);
+
+        while (match) {
+            value = value.replace(match[0], "");
+            matches.push(match[0].replace(/"/g, ''));
+            match = value.match(regex);
+        }
+
+        const terms = value.trim().split(/(\s+)/).concat(matches);
         terms.forEach(term => {
             if (term !== " ") {
                 this.addCondition("any", "ilike", term, "%", "%");
@@ -83,9 +103,7 @@ export class FilterBuilder {
                 ? resourcePrefix + "."
                 : "";
 
-            const fld = field.indexOf('properties.') < 0 ? _.snakeCase(field) : field;
-
-            this.filters += `${this.filters ? "," : ""}["${resPrefix}${fld}","${cond}",${value}]`;
+            this.filters += `${this.filters ? "," : ""}["${resPrefix}${field}","${cond}",${value}]`;
         }
         return this;
     }