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  };