methods/merge-filter.js

import * as merge from 'lodash.merge'

const getNestedRule = (filter, occur) => {
  // treat nested query object
  // look './set-specs.js' as example
  const query = filter && filter.nested && filter.nested.query
  if (query && query.bool && Array.isArray(query.bool[occur])) {
    return query.bool[occur].find(rule => rule.term)
  }
  return undefined
}

export default (self, filter, occur = 'filter') => {
  const type = Object.keys(filter)[0]
  // merge general filter to current Query DSL
  // ensure query.bool.filter is array
  merge(self.dsl, {
    query: {
      bool: {
        [occur]: []
      }
    }
  })
  const filters = self.dsl.query.bool[occur]

  // check if new filter object should be added or replace other
  let rule
  switch (type) {
    case 'terms':
    case 'term':
    case 'multi_match':
    case 'range':
      if (typeof filter[type] === 'object' && filter[type] !== null) {
        const field = Object.keys(filter[type])[0]
        for (let i = 0; i < filters.length; i++) {
          // check if is the same field
          const rule = filters[i][Object.keys(filters[i])[0]]
          if (typeof rule === 'object' && rule !== null && Object.keys(rule)[0] === field) {
            // replace filter object
            filters[i] = filter
            return self
          }
        }
      }
      break

    case 'nested':
      rule = getNestedRule(filter, occur)
      if (rule) {
        // check field and key value for nested object filter
        const field = Object.keys(rule.term)[0]
        const key = rule.term[field]
        // run root filters list first
        for (let i = 0; i < filters.length; i++) {
          const rule = getNestedRule(filters[i], occur)
          // check field and key value
          if (rule && rule.term[field] === key) {
            // replace filter object
            filters[i] = filter
            return self
          }
        }
      }
  }

  // add new filter object to current list
  filters.push(filter)
  return self
}

/**
 * @method
 * @name EcomSearch#mergeFilter
 * @description Add/update a filter object on current
 * Query DSL filters list for next search request.
 *
 * @param {object} filter - A valid object for Query DSL
 * [filter context]{@link https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html}
 * @param {string} [occur='filter'] - Occurrence type for
 * [boolean query]{@link https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html}
 * @returns {self}
 *
 * @example

// Add custom filter by product quantity
search.mergeFilter({
  term: {
    quantity: 0
  }
})

 * @example

// Set filter by SKUs and run search request
const filter = { terms: { sku: ['nb-apl-1203', 'hd-csr-303'] } }
search.mergeFilter(filter).fetch()

 */