ICIJ/datashare-client

View on GitHub
src/store/filters/FilterText.js

Summary

Maintainability
A
2 hrs
Test Coverage
import get from 'lodash/get'
import includes from 'lodash/includes'
import some from 'lodash/some'

// Private properties keys
const _VALUES = typeof Symbol === 'function' ? Symbol('_values') : '_values'
const _ROOT_STATE = typeof Symbol === 'function' ? Symbol('_ROOT_state') : '_ROOT_state'

export default class FilterText {
  constructor({
    name,
    key,
    icon = null,
    isSearchable = false,
    alternativeSearch = () => {},
    order = null,
    fromElasticSearch = true,
    preference = '_local',
    forceExclude = false
  } = {}) {
    this.name = name
    this.key = key
    this.icon = icon
    this.isSearchable = isSearchable
    this.component = 'FilterText'
    this.alternativeSearch = alternativeSearch
    this.order = order
    this.fromElasticSearch = fromElasticSearch
    this.preference = preference
    this.forceExclude = forceExclude
  }

  itemParam(item) {
    return { name: this.name, value: item.key }
  }

  itemLabel(item) {
    return item.key || item.value
  }

  addChildIncludeFilter(body, param) {
    return body.addFilter('terms', this.key, param.values)
  }

  addParentIncludeFilter(body, param) {
    return body.query('has_parent', { parent_type: 'Document' }, (q) => q.query('terms', this.key, param.values))
  }

  addParentExcludeFilter(body, param) {
    return body.query('has_parent', { parent_type: 'Document' }, (q) => q.notQuery('terms', this.key, param.values))
  }

  addChildExcludeFilter(body, param) {
    return body.notFilter('terms', this.key, param.values)
  }

  body(body, options, from = 0, size = 8) {
    return body.query('match', 'type', 'Document').agg(
      'terms',
      this.key,
      this.key,
      (sub) => {
        return sub.agg(
          'bucket_sort',
          {
            size,
            from
          },
          'bucket_truncate'
        )
      },
      options
    )
  }

  addFilter(body) {
    if (this.hasValues()) {
      const filterType = this.isNamedEntityAggregation(body) ? 'Parent' : 'Child'
      const exclude = this.reverse || this.forceExclude
      const filterName = exclude ? 'Exclude' : 'Include'
      const options = { name: this.name, values: this.values, reverse: exclude }
      const method = `add${filterType}${filterName}Filter`
      return this[method] ? this[method](body, options) : null
    }
  }

  hasValues() {
    return this.values.length > 0
  }

  isNamedEntityAggregation(body) {
    return some(['"must":{"term":{"type":"NamedEntity"}}', '"must":[{"term":{"type":"NamedEntity"}}'], (str) =>
      includes(JSON.stringify(body.build()), str)
    )
  }

  applyTo(body) {
    this.addFilter(body)
  }

  bindRootState(rootState) {
    this[_ROOT_STATE] = this[_ROOT_STATE] || rootState
  }

  get rootState() {
    return this[_ROOT_STATE]
  }

  get state() {
    return this?.rootState?.search
  }

  get values() {
    return this[_VALUES] || get(this, ['state', 'values', this.name], [])
  }

  set values(values) {
    this[_VALUES] = values
  }

  setValues(values) {
    this.values = values
    return this
  }

  get reverse() {
    return get(this, ['state', 'reversedFilters'], []).indexOf(this.name) > -1
  }

  get contextualized() {
    return get(this, ['state', 'contextualizedFilters'], []).indexOf(this.name) > -1
  }

  get sortBy() {
    return get(this, ['state', 'sortedFilters', this.name, 'sortBy'], '_count')
  }

  get sortByOrder() {
    return get(this, ['state', 'sortedFilters', this.name, 'sortByOrder'], 'desc')
  }
}