vitess.io/vitess@v0.16.2/web/vtadmin/src/util/filterNouns.ts (about) 1 /** 2 * Copyright 2021 The Vitess Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import { partition } from 'lodash-es'; 18 import { KeyValueSearchToken, SearchTokenTypes, tokenizeSearch } from './tokenize'; 19 20 /** 21 * `filterNouns` filters a list of nouns by a search string. 22 */ 23 export const filterNouns = <T extends { [k: string]: any }>(needle: string | null | undefined, haystack: T[]): T[] => { 24 if (!needle) return haystack; 25 26 const tokens = tokenizeSearch(needle); 27 28 // Separate key/value tokens (which can be additive or subtractive depending on whether the 29 // key is the same) vs. other tokens (e.g., fuzzy/exact string filters, which are always additive). 30 const [kvTokens, otherTokens] = partition(tokens, (t) => t.type === SearchTokenTypes.KEY_VALUE); 31 32 // This assumes that values are ALWAYS exact matches. 33 const kvMap = (kvTokens as KeyValueSearchToken[]).reduce((acc, t) => { 34 if (!(t.key in acc)) acc[t.key] = []; 35 acc[t.key].push(t.value.toLowerCase()); 36 return acc; 37 }, {} as { [key: string]: string[] }); 38 39 // Filter out key/value tokens first 40 const results = haystack.filter((noun: T) => { 41 // Every specified key must map to a matching value for at least one property 42 return Object.entries(kvMap).every(([k, vs]) => { 43 const nv = noun[k]; 44 if (!nv) return false; 45 46 // Key/value matches always use an exact (never fuzzy), 47 // case insensitive match 48 return vs.indexOf(nv.toLowerCase()) >= 0; 49 }); 50 }); 51 52 return otherTokens.reduce((acc, token) => { 53 switch (token.type) { 54 case SearchTokenTypes.EXACT: 55 const needle = token.value; 56 return acc.filter((o: T) => { 57 return Object.values(o).some((val) => val === needle); 58 }); 59 case SearchTokenTypes.FUZZY: 60 return acc.filter((o: T) => { 61 return Object.values(o).some((val) => fuzzyCompare(token.value, `${val}`)); 62 }); 63 default: 64 return acc; 65 } 66 }, results as T[]); 67 }; 68 69 const fuzzyCompare = (needle: string, haystack: string) => { 70 return `${haystack}`.toLowerCase().indexOf(needle.toLowerCase()) >= 0; 71 };