diff --git a/lib/FilterGroups/FilterGroups.js b/lib/FilterGroups/FilterGroups.js index ef0bc067..ac3622e9 100644 --- a/lib/FilterGroups/FilterGroups.js +++ b/lib/FilterGroups/FilterGroups.js @@ -1,4 +1,7 @@ -import kebabCase from 'lodash/kebabCase'; +import { + kebabCase, + template, +} from 'lodash'; import React from 'react'; import PropTypes from 'prop-types'; import FilterControlGroup from '../FilterControlGroup'; @@ -117,6 +120,19 @@ export function filterState(filters) { * selected have no effect unless the `restrictWhenAllSelected` * setting is on. * + * config objects may have the following attributes: + * * label: a FormattedMessage suitable for display (i.e. translatable) + * * name: the key used to match a config-object to a filter value + * * cql: the field to match against when constructing 'field="value"' queries + * or range queries such as'field>="valueA" and field<="valueB"' + * * template: an alternative to the cql attribute, the value is a lodash + * template string that will have values interpolated into it. this allows + * for the construction of arbitrarily complex CQL queries + * * values: array of [cql, name, hidden] objects corresponding to a filter's potential + * values where cql represents the key to use in a query and name represents + * the value to use in display. [cql] will match a filter's value and thus + * is the only attribute relevant here. Details on [hidden] are below. + * * config values with the attribute "hidden: true" are not exposed in the UI * (i.e. no checkbox is presented in the UI) but the filter value is always * applied. For example, suppose a filter group "species" existed and the @@ -125,7 +141,7 @@ export function filterState(filters) { * values entry with the attribute `name: "human"` would also contain the * attribute `hidden: true`. * - * @param config array list of objects with keys label, name, cql, values[] + * @param config array list of objects with keys label, name, cql|template, values[] * @param filters string comma-delimited list of active filters, e.g. foo.someValue,bar.otherValue * */ @@ -172,7 +188,6 @@ export function filters2cql(config, filters = '') { // with the same name. that *shouldn't* happen, but just in case it // does, only use the first one. const group = config.filter(g => g.name === groupName)[0]; - const cqlIndex = group.cql; // values contains the selected filters const values = groups[groupName]; @@ -183,17 +198,21 @@ export function filters2cql(config, filters = '') { return (obj.length > 0) ? obj[0].cql : v; }); - if (group.isRange) { + if (group.isRange && group.cql) { const { isIncludingStart = true, isIncludingEnd = true, rangeSeparator = ':' } = group; const [start, end] = mappedValues[0].split(rangeSeparator); - const startCondition = `${cqlIndex}>${isIncludingStart ? '=' : ''}"${start}"`; - const endCondition = `${cqlIndex}<${isIncludingEnd ? '=' : ''}"${end}"`; - + const startCondition = `${group.cql}>${isIncludingStart ? '=' : ''}"${start}"`; + const endCondition = `${group.cql}<${isIncludingEnd ? '=' : ''}"${end}"`; conds.push(`(${startCondition} and ${endCondition})`); - } else { + } else if (group.template) { let joinedValues = mappedValues.map(v => `"${v}"`).join(' or '); if (values.length > 1) joinedValues = `(${joinedValues})`; - conds.push(`${cqlIndex}=${joinedValues}`); + const clause = template(group.template)({ value: joinedValues }); + conds.push(`(${clause})`); + } else if (group.cql) { + let joinedValues = mappedValues.map(v => `"${v}"`).join(' or '); + if (values.length > 1) joinedValues = `(${joinedValues})`; + conds.push(`${group.cql}=${joinedValues}`); } } }