All files / src/processors AltQuerySuggester.js

100% Statements 38/38
89.47% Branches 17/19
100% Functions 9/9
100% Lines 38/38
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104    9x 9x 9x   9x           9x 9x 9x         2x               5x 5x   24x   24x   24x     24x   24x   52x 52x                   4x           12x 12x   2x   10x   2x   8x   2x       6x     12x   4x         6x 6x 6x   60x 60x   14x 14x     6x         9x   9x    
"use strict";
 
const Processor = require('./Processor');
const extractObjectValues = require('../misc/extractObjectValues');
const levenshtein = require('fast-levenshtein')
 
const PROCESSOR_TYPE = 'altquery';
 
class AltQuerySuggester extends Processor
{
  constructor(config = {})
  {
    super(config, PROCESSOR_TYPE, ['add', 'results']);
    this.fields = this.fields || [];
    this.words = this.words || {};
  }
 
  async state()
  {
    return Object.assign({
      fields: this.fields,
      words: this.words,
    }, await super.state());
  }
 
  async addDocuments(system, documentIndices, documents)
  {
    const words = this.words;
    for (const document of documents)
    {
      extractObjectValues(document, this.fields, (newValue, field, scale) =>
      {
        Eif (typeof newValue === 'string')
        {
          newValue = newValue.toLowerCase()
            .replace(/[^a-z]+/g, ' ')
            .trim();
          Eif (newValue)
          {
            for (let word of newValue.split(/\s+/g))
            {
              words[word] = words[word] || 0;
              words[word]++;
            }
          }
        }
      });
    }
  }
 
  async processResults(system, query, results)
  {
    results.altKeywords = [results.keywords.map(kw =>
      {
        const
        {
          keyword,
          hits
        } = kw;
        if (hits)
        {
          return keyword;
        }
        else if (keyword === 'and')
        {
          return 'and'
        }
        else if (keyword === 'or')
        {
          return 'or'
        }
        else
        {
          return this.getClosest(keyword);
        }
      })
      .filter(i => i)
      .join(' ')
    ].filter(i => i);
  }
 
  getClosest(word)
  {
    let closest, closestDiff = 100;
    const dist = levenshtein.get.bind(levenshtein);
    for (const target of Object.keys(this.words))
    {
      const diff = dist(word, target) / Math.min(word.length, target.length);
      if (diff < closestDiff)
      {
        closestDiff = diff;
        closest = target;
      }
    }
    return closest;
  }
 
}
 
module.exports = AltQuerySuggester;
// register type for serialisation
require('./register')
  .add(AltQuerySuggester, PROCESSOR_TYPE);