From e84e1182fb0683516a9fef41eccebd12587e5701 Mon Sep 17 00:00:00 2001 From: Mark Rieth Date: Fri, 18 Dec 2020 16:31:55 -0800 Subject: [PATCH 1/6] Add query string filter --- dev/app/features/query_string_filter.tsx | 0 src/filters/index.ts | 2 +- src/filters/query_string_filter.ts | 427 +++++++++++++++++++++++ src/types.ts | 17 + 4 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 dev/app/features/query_string_filter.tsx create mode 100644 src/filters/query_string_filter.ts diff --git a/dev/app/features/query_string_filter.tsx b/dev/app/features/query_string_filter.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/filters/index.ts b/src/filters/index.ts index ff18120..a458218 100644 --- a/src/filters/index.ts +++ b/src/filters/index.ts @@ -6,7 +6,7 @@ export {default as MultiSelectFilter} from './multi_select_filter'; export {default as DateRangeFilter} from './date_range_filter'; export {default as GeoFilter} from './geo_filter'; export {default as TermsFilter} from './terms_filter'; - +export {default as QueryStringFilter} from './query_string_filter'; export {default as filterUtils} from './utils'; import { diff --git a/src/filters/query_string_filter.ts b/src/filters/query_string_filter.ts new file mode 100644 index 0000000..3f861c5 --- /dev/null +++ b/src/filters/query_string_filter.ts @@ -0,0 +1,427 @@ +import {runInAction, decorate, observable, set, reaction} from 'mobx'; +import {objKeys} from '../utils'; +import { + ESRequest, + ESResponse, + FilterKind, + BaseFilterConfig, + IBaseOptions, + ESMappingType, + QueryStringSubFieldFilterValue, + QueryStringFieldFilter, + FieldFilters, + FieldNameModifier, + FieldKinds, RawMultiSelectAggs +} from '../types'; +import BaseFilter from './base'; +import utils from './utils'; + +/** + * Config + */ +const CONFIG_DEFAULT = { + defaultFilterKind: 'should', + defaultFilterInclusion: 'include', + getCount: true, + aggsEnabled: false, + fieldNameModifierQuery: (fieldName: string) => fieldName, + fieldNameModifierAggs: (fieldName: string) => fieldName +}; + +export interface IConfig extends BaseFilterConfig { + field: string; + defaultFilterKind?: 'should' | 'must'; + defaultFilterInclusion?: 'include' | 'exclude'; + aggsEnabled?: boolean; + getCount?: boolean; + fieldNameModifierQuery?: FieldNameModifier; + fieldNameModifierAggs?: FieldNameModifier; +} + +export type IConfigs = { + [esFieldName in Fields]: IConfig; +}; + +/** + * Results + */ + +export type QueryStringCountResult = { + [selectedValue: string]: number; +}; + +export type CountResults = { + [esFieldName in Fields]: QueryStringCountResult; +}; + +export const shouldUseField = (_fieldName: string, fieldType: ESMappingType) => + fieldType === 'keyword' || fieldType === 'text'; + +class QueryStringFilter extends BaseFilter { + public filteredCount: CountResults; + public unfilteredCount: CountResults; + + constructor( + defaultConfig?: Omit, 'field'>, + specificConfigs?: IConfigs, + options?: IBaseOptions + ) { + super( + 'query_string', + defaultConfig || (CONFIG_DEFAULT as Omit, 'field'>), + specificConfigs as IConfigs + ); + runInAction(() => { + this._shouldUseField = (options && options.shouldUseField) || shouldUseField; + this.filteredCount = {} as CountResults; + this.unfilteredCount = {} as CountResults; + }); + + reaction( + () => { + const filteredCountFieldNames = objKeys(this.filteredCount); + + const fieldsMissingUnfilteredCounts = filteredCountFieldNames.reduce( + (acc, fieldName) => { + const filteredSubFieldNameValues = Object.keys( + this.filteredCount[fieldName] || {} + ); + const unfilteredSubFieldNameObj = this.unfilteredCount[fieldName] || {}; + + const fieldIsMissingUnfilteredCounts = filteredSubFieldNameValues.reduce( + (missingUnfilteredCounts, name) => { + if (unfilteredSubFieldNameObj[name] === undefined) { + return true; + } else { + return missingUnfilteredCounts; + } + }, + false + ); + + if (fieldIsMissingUnfilteredCounts) { + return [...acc, fieldName]; + } else { + return acc; + } + }, + [] as string[] + ); + + return fieldsMissingUnfilteredCounts; + }, + fieldsMissingUnfilteredCounts => { + if (fieldsMissingUnfilteredCounts && fieldsMissingUnfilteredCounts.length > 0) { + fieldsMissingUnfilteredCounts.forEach(field => { + this._shouldUpdateUnfilteredAggsSubscribers.forEach(s => + s(this.filterKind, field as Fields) + ); + }); + } + } + ); + } + + public userState(): { + fieldKinds?: FieldKinds; + fieldFilters?: FieldFilters; + } | void { + const kinds = Object.keys(this.fieldFilters).reduce((fieldKinds, fieldName) => { + return { + ...fieldKinds, + [fieldName]: this.kindForField(fieldName as Fields) + }; + }, {} as FieldKinds); + + const fieldFilters = Object.keys(this.fieldFilters).reduce((fieldFilterAcc, fieldName) => { + const filter = this.fieldFilters[fieldName as Fields] as QueryStringFieldFilter; + if (filter && Object.keys(filter).length > 0) { + return { + ...fieldFilterAcc, + [fieldName]: filter + }; + } else { + return fieldFilterAcc; + } + }, {} as FieldFilters); + + if (Object.keys(kinds).length > 0 && Object.keys(fieldFilters).length > 0) { + return { + fieldKinds: kinds, + fieldFilters + }; + } else if (Object.keys(kinds).length > 0) { + return { + fieldKinds: kinds + }; + } else if (Object.keys(fieldFilters).length > 0) { + return { + fieldFilters + }; + } else { + return; + } + } + + /** + * Alias to getter b/c computed getters can't be inherited + */ + + public get fields() { + return this._fields; + } + /** + * Alias to getter b/c computed getters can't be inherited + */ + public get activeFields() { + return this._activeFields; + } + + /** + * Clears all field filters for this filter. + * Clears all state related to aggregations. + */ + public clearAllFieldFilters = () => { + runInAction(() => { + this.fieldFilters = {} as FieldFilters; + this.filteredCount = {} as CountResults; + this.unfilteredCount = {} as CountResults; + }); + }; + + /** + * Sets a sub filter for a field. + */ + public addToFilter( + field: Fields, + subFilterName: string, + subFilterValue: QueryStringSubFieldFilterValue + ): void { + runInAction(() => { + const subFilters = this.fieldFilters[field]; + const newSubFilters = { + ...subFilters, + [subFilterName]: subFilterValue + }; + set(this.fieldFilters, { + [field]: newSubFilters + }); + }); + } + + /** + * Deletes a sub filter for a field. + */ + public removeFromFilter(field: Fields, subFilterName: string): void { + runInAction(() => { + const subFilters = this.fieldFilters[field]; + if (!subFilters) { + return; + } + + delete subFilters[subFilterName]; + + set(this.fieldFilters, { + [field]: subFilters + }); + }); + } + + /** + * State that should cause a global ES query request using all filters + * + * Changes to this state is tracked by the manager so that it knows when to run a new filter query + */ + public get _shouldRunFilteredQueryAndAggs(): object { + // tslint:disable-next-line:no-shadowed-variable + const fieldFilters = objKeys(this.fieldFilters).reduce((fieldFilters, fieldName) => { + const subFields = this.fieldFilters[fieldName] as QueryStringFieldFilter; + if (!subFields) { + return {...fieldFilters}; + } + // access sub field filters so those changes are tracked too + // tslint:disable-next-line:no-shadowed-variable + const subFieldFilters = Object.keys(subFields).reduce((subFieldFilters, subFieldName) => { + return { + ...subFieldFilters, + [`_$_${fieldName}-${subFieldName}`]: subFields[subFieldName] + }; + }, {} as QueryStringFieldFilter); + return {...fieldFilters, ...subFieldFilters}; + }, {}); + return {filters: {...fieldFilters}, kinds: {...this.fieldKinds}}; + } + + /** + * *************************************************************************** + * REQUEST BUILDERS + * *************************************************************************** + */ + + /** + * Transforms the request obj. + * + * Adds aggs to the request, but no query. + */ + public _addUnfilteredQueryAndAggsToRequest = (request: ESRequest): ESRequest => { + return [this._addCountAggsToEsRequest].reduce((newRequest, fn) => fn(newRequest), request); + }; + + /** + * Transforms the request obj. + * + * Adds aggs to the request, but no query. + */ + public _addUnfilteredAggsToRequest = ( + request: ESRequest, + fieldToFilterOn: string + ): ESRequest => { + return [this._addCountAggsToEsRequest].reduce( + (newRequest, fn) => fn(newRequest, fieldToFilterOn), + request + ); + }; + + /** + * Transforms the request obj. + * + * Adds aggs to the request, but no query. + */ + public _addFilteredAggsToRequest = (request: ESRequest, fieldToFilterOn: string): ESRequest => { + return [this._addQueriesToESRequest, this._addCountAggsToEsRequest].reduce( + (newRequest, fn) => fn(newRequest, fieldToFilterOn), + request + ); + }; + + /** + * Transforms the request obj. + * + * Adds query and aggs to the request. + */ + public _addFilteredQueryAndAggsToRequest = (request: ESRequest): ESRequest => { + return [this._addQueriesToESRequest, this._addCountAggsToEsRequest].reduce( + (newRequest, fn) => fn(newRequest), + request + ); + }; + + /** + * Transforms the request obj. + * + * Adds query to the request, but no aggs. + */ + public _addFilteredQueryToRequest = (request: ESRequest): ESRequest => { + return [this._addQueriesToESRequest].reduce((newRequest, fn) => fn(newRequest), request); + }; + + /** + * *************************************************************************** + * RESPONSE PARSERS + * *************************************************************************** + */ + + /** + * Extracts unfiltered agg stats from a response obj. + */ + public _extractUnfilteredAggsStateFromResponse = (response: ESResponse): void => { + [this._parseCountFromResponse].forEach(fn => fn(true, response)); + }; + + /** + * Extracts filtered agg stats from a response obj. + */ + public _extractFilteredAggsStateFromResponse = (response: ESResponse): void => { + [this._parseCountFromResponse].forEach(fn => fn(false, response)); + }; + + /** + * *************************************************************************** + * CUSTOM TO TEMPLATE + * *************************************************************************** + */ + + public _addQueriesToESRequest = (request: ESRequest): ESRequest => { + if (!this.fieldFilters) { + return request; + } + // tslint:disable-next-line + return objKeys(this.fieldConfigs).reduce((acc, fieldName) => { + if (!this.fieldFilters) { + return acc; + } + const config = this.fieldConfigs[fieldName]; + const name = config.field; + + const filter = this.fieldFilters[fieldName]; + if (!filter) { + return acc; + } + + const kind = this.kindForField(fieldName); + if (!kind) { + throw new Error(`kind is not set for query_string filter type ${fieldName}`); + } + + const fieldNameModifier = config.fieldNameModifierQuery; + + if (filter) { + return objKeys(filter as QueryStringFieldFilter).reduce((newQuery, selectedValue) => { + const selectedValueFilter = filter[selectedValue]; + const queryString = selectedValueFilter.query_string; + + const inclusion = + selectedValueFilter.inclusion || config.defaultFilterInclusion; + + const newFilter = + inclusion === 'include' + ? {query_string: {[fieldNameModifier(name)]: queryString}} + : { + bool: { + must_not: { + query_string: {[fieldNameModifier(name)]: queryString} + } + } + }; + const kindForSelectedValue = selectedValueFilter.kind || kind; + const existingFiltersForKind = + newQuery.query.bool[kindForSelectedValue as FilterKind] || []; + + return { + ...newQuery, + query: { + ...newQuery.query, + bool: { + ...newQuery.query.bool, + [kindForSelectedValue as FilterKind]: [ + ...existingFiltersForKind, + newFilter + ] + } + } + }; + }, acc); + } else { + return acc; + } + }, request); + }; + + + public _addCountAggsToEsRequest = (request: ESRequest, _fieldToFilterOn?: string): ESRequest => { + return request; + }; + + public _parseCountFromResponse = (_isUnfilteredQuery: boolean, _response: ESResponse): void => {}; + + +} + +decorate(QueryStringFilter, { + filteredCount: observable, + unfilteredCount: observable +}); + +utils.decorateFilter(QueryStringFilter); + +export default QueryStringFilter; diff --git a/src/types.ts b/src/types.ts index 0a85372..fa3eef8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,6 +10,7 @@ import { TermsFilter } from './filters'; import {FuzzySuggestion, BaseSuggestion} from './suggestions'; +import QueryStringFilter from './filters/query_string_filter'; /** * *********************************** @@ -269,6 +270,7 @@ export interface IFiltersOptions { exists?: ExistsFilter; geo?: GeoFilter; terms?: TermsFilter; + queryString: QueryStringFilter [customFilter: string]: BaseFilter | undefined; } @@ -286,6 +288,7 @@ export interface IFilters { exists: ExistsFilter; geo: GeoFilter; terms: TermsFilter; + queryString: QueryStringFilter [customFilter: string]: BaseFilter; } @@ -325,6 +328,20 @@ export type RawMultiSelectAggs = { }>; }; +/** + * Query String Filter + */ + +export type QueryStringSubFieldFilterValue = { + inclusion: 'include' | 'exclude'; + kind?: 'should' | 'must'; + query_string: string; +}; + +export type QueryStringFieldFilter = { + [selectedValue: string]: QueryStringSubFieldFilterValue; +}; + /** * Terms Filter */ From adbd02013929c850aedccf6d5f8a7d9ff1672e1f Mon Sep 17 00:00:00 2001 From: Mark Rieth Date: Fri, 18 Dec 2020 16:40:03 -0800 Subject: [PATCH 2/6] Add view for query string filter --- dev/app/features/query_string_filter.tsx | 28 ++++++++++++++++++++++++ src/filters/query_string_filter.ts | 6 +++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/dev/app/features/query_string_filter.tsx b/dev/app/features/query_string_filter.tsx index e69de29..6ff61ec 100644 --- a/dev/app/features/query_string_filter.tsx +++ b/dev/app/features/query_string_filter.tsx @@ -0,0 +1,28 @@ +import React, {useContext} from 'react'; +import Context from '../context'; + +export type QueryStringFilterProps = { + filterName: string +} +const QueryStringFilter: React.FC = (props) => { + const { filterName } = props; + const creatorCRM = useContext(Context.creatorCRM); + if (!filterName) { + return null; + } + const { + filters: {queryString: queryStringFilter} + } = creatorCRM; + + return ( + <> +
Query String Filter
+ + + ); +}; +export default QueryStringFilter diff --git a/src/filters/query_string_filter.ts b/src/filters/query_string_filter.ts index 3f861c5..0d79305 100644 --- a/src/filters/query_string_filter.ts +++ b/src/filters/query_string_filter.ts @@ -11,7 +11,7 @@ import { QueryStringFieldFilter, FieldFilters, FieldNameModifier, - FieldKinds, RawMultiSelectAggs + FieldKinds } from '../types'; import BaseFilter from './base'; import utils from './utils'; @@ -412,7 +412,9 @@ class QueryStringFilter extends BaseFilter {}; + public _parseCountFromResponse = (_isUnfilteredQuery: boolean, _response: ESResponse): void => { + return; + }; } From 31979c4467bc47fca21ada9037f521923ecf064e Mon Sep 17 00:00:00 2001 From: Mark Rieth Date: Mon, 21 Dec 2020 11:17:26 -0800 Subject: [PATCH 3/6] Fix ES query for querystring filter --- dev/app/context.ts | 21 +++- dev/app/features/index.ts | 1 + dev/app/features/query_string_filter.tsx | 29 +++-- dev/app/index.tsx | 3 +- src/filters/query_string_filter.ts | 149 +++++++---------------- src/types.ts | 8 +- 6 files changed, 89 insertions(+), 122 deletions(-) diff --git a/dev/app/context.ts b/dev/app/context.ts index 467ba59..3ee8314 100644 --- a/dev/app/context.ts +++ b/dev/app/context.ts @@ -19,6 +19,7 @@ import { } from '../../src'; import {IRangeConfig} from '../../src/filters/range_filter'; import {reaction} from 'mobx'; +import QueryStringFilter from '../../src/filters/query_string_filter'; // import {toJS} from 'mobx'; const exampleFormInstance = new ExampleForm(); @@ -77,6 +78,22 @@ const customTermsFilter = new TermsFilter({ fieldNameModifierAggs: (fieldName: string) => `${fieldName}.keyword` }); +const customQueryStringFilter = new QueryStringFilter({ + defaultFilterInclusion: 'include', + defaultFilterKind: 'must', + getCount: false, + aggsEnabled: false, + fieldNameModifierQuery: (fieldName: string) => fieldName, + fieldNameModifierAggs: (fieldName: string) => fieldName +}, { + 'user.age': { + field: 'user.age', + }, + 'user_profile.age': { + field: 'user_profile.age', + } +}) + const defaultRangeFilterConfig: IRangeConfig = { field: '', aggsEnabled: false, @@ -98,7 +115,6 @@ const customRangeFilterConfig = { }; const customRangeFilter = new RangeFilter(defaultRangeFilterConfig as any, customRangeFilterConfig); - const client = new AxiosESClient(process.env.ELASTIC_SEARCH_ENDPOINT); // const client = new CreatorIndexGQLClient(gqlClient); const creatorCRM = new Manager(client, { @@ -117,7 +133,8 @@ const creatorCRM = new Manager(client, { // ], filters: { range: customRangeFilter, - terms: customTermsFilter + terms: customTermsFilter, + queryString: customQueryStringFilter }, suggestions: { prefix: customPrefixSuggestion diff --git a/dev/app/features/index.ts b/dev/app/features/index.ts index 0e7229c..f2efcf2 100644 --- a/dev/app/features/index.ts +++ b/dev/app/features/index.ts @@ -10,3 +10,4 @@ export {default as CustomQuery} from './custom_query'; export {default as ExistsFilter} from './exists_filter'; export {default as MultiSelectFilter} from './multi_select_filter'; export {default as HistoryNav} from './history_nav'; +export {default as QueryStringFilter} from './query_string_filter'; diff --git a/dev/app/features/query_string_filter.tsx b/dev/app/features/query_string_filter.tsx index 6ff61ec..a4bc308 100644 --- a/dev/app/features/query_string_filter.tsx +++ b/dev/app/features/query_string_filter.tsx @@ -1,28 +1,37 @@ -import React, {useContext} from 'react'; +import React, {useContext, useState} from 'react'; import Context from '../context'; export type QueryStringFilterProps = { - filterName: string + field: string } + +// tslint:disable-next-line:variable-name const QueryStringFilter: React.FC = (props) => { - const { filterName } = props; + const { field } = props; const creatorCRM = useContext(Context.creatorCRM); - if (!filterName) { - return null; - } + const [queryStringInput, setQueryStringInput] = useState(''); + const { filters: {queryString: queryStringFilter} } = creatorCRM; return ( - <> -
Query String Filter
+
+
Query String Filter for {field}
+
- +
+ +
); }; export default QueryStringFilter diff --git a/dev/app/index.tsx b/dev/app/index.tsx index c5715e8..e88b0d3 100644 --- a/dev/app/index.tsx +++ b/dev/app/index.tsx @@ -13,7 +13,7 @@ import { Suggestion, ExistsFilter, MultiSelectFilter, - HistoryNav + HistoryNav, QueryStringFilter } from './features'; const Main = styled.div` @@ -63,6 +63,7 @@ export default () => ( {/* {filterName => } */} + diff --git a/src/filters/query_string_filter.ts b/src/filters/query_string_filter.ts index 0d79305..bd411c5 100644 --- a/src/filters/query_string_filter.ts +++ b/src/filters/query_string_filter.ts @@ -1,4 +1,4 @@ -import {runInAction, decorate, observable, set, reaction} from 'mobx'; +import {runInAction, decorate, observable, reaction} from 'mobx'; import {objKeys} from '../utils'; import { ESRequest, @@ -7,7 +7,6 @@ import { BaseFilterConfig, IBaseOptions, ESMappingType, - QueryStringSubFieldFilterValue, QueryStringFieldFilter, FieldFilters, FieldNameModifier, @@ -57,7 +56,11 @@ export type CountResults = { export const shouldUseField = (_fieldName: string, fieldType: ESMappingType) => fieldType === 'keyword' || fieldType === 'text'; -class QueryStringFilter extends BaseFilter { +class QueryStringFilter extends BaseFilter< + Fields, + IConfig, + QueryStringFieldFilter +> { public filteredCount: CountResults; public unfilteredCount: CountResults; @@ -189,67 +192,13 @@ class QueryStringFilter extends BaseFilter { - const subFilters = this.fieldFilters[field]; - const newSubFilters = { - ...subFilters, - [subFilterName]: subFilterValue - }; - set(this.fieldFilters, { - [field]: newSubFilters - }); - }); - } - - /** - * Deletes a sub filter for a field. - */ - public removeFromFilter(field: Fields, subFilterName: string): void { - runInAction(() => { - const subFilters = this.fieldFilters[field]; - if (!subFilters) { - return; - } - - delete subFilters[subFilterName]; - - set(this.fieldFilters, { - [field]: subFilters - }); - }); - } - /** * State that should cause a global ES query request using all filters * * Changes to this state is tracked by the manager so that it knows when to run a new filter query */ public get _shouldRunFilteredQueryAndAggs(): object { - // tslint:disable-next-line:no-shadowed-variable - const fieldFilters = objKeys(this.fieldFilters).reduce((fieldFilters, fieldName) => { - const subFields = this.fieldFilters[fieldName] as QueryStringFieldFilter; - if (!subFields) { - return {...fieldFilters}; - } - // access sub field filters so those changes are tracked too - // tslint:disable-next-line:no-shadowed-variable - const subFieldFilters = Object.keys(subFields).reduce((subFieldFilters, subFieldName) => { - return { - ...subFieldFilters, - [`_$_${fieldName}-${subFieldName}`]: subFields[subFieldName] - }; - }, {} as QueryStringFieldFilter); - return {...fieldFilters, ...subFieldFilters}; - }, {}); - return {filters: {...fieldFilters}, kinds: {...this.fieldKinds}}; + return {filters: {...this.fieldFilters}, kinds: {...this.fieldKinds}}; } /** @@ -346,16 +295,16 @@ class QueryStringFilter extends BaseFilter { + return objKeys(this.fieldConfigs).reduce((esRequest, fieldName) => { if (!this.fieldFilters) { - return acc; + return esRequest; } const config = this.fieldConfigs[fieldName]; const name = config.field; - const filter = this.fieldFilters[fieldName]; + const filter = this.fieldFilters[fieldName] as QueryStringFieldFilter; if (!filter) { - return acc; + return esRequest; } const kind = this.kindForField(fieldName); @@ -363,60 +312,54 @@ class QueryStringFilter extends BaseFilter { - const selectedValueFilter = filter[selectedValue]; - const queryString = selectedValueFilter.query_string; - - const inclusion = - selectedValueFilter.inclusion || config.defaultFilterInclusion; - - const newFilter = - inclusion === 'include' - ? {query_string: {[fieldNameModifier(name)]: queryString}} - : { - bool: { - must_not: { - query_string: {[fieldNameModifier(name)]: queryString} - } - } - }; - const kindForSelectedValue = selectedValueFilter.kind || kind; - const existingFiltersForKind = - newQuery.query.bool[kindForSelectedValue as FilterKind] || []; - - return { - ...newQuery, - query: { - ...newQuery.query, - bool: { - ...newQuery.query.bool, - [kindForSelectedValue as FilterKind]: [ - ...existingFiltersForKind, - newFilter - ] - } + const query = filter.query; + const inclusion = filter.inclusion ?? config.defaultFilterInclusion; + + // See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html + const newFilter = + inclusion === 'include' + ? {query_string: {query, default_field: name}} + : { + bool: { + must_not: { + query_string: {query, default_field: name} + } + } + }; + const kindForSelectedValue = filter.kind ?? kind; + const existingFiltersForKind = + esRequest.query.bool[kindForSelectedValue as FilterKind] ?? []; + + return { + ...esRequest, + query: { + ...esRequest.query, + bool: { + ...esRequest.query.bool, + [kindForSelectedValue as FilterKind]: [ + ...existingFiltersForKind, + newFilter + ] } - }; - }, acc); + } + }; } else { - return acc; + return esRequest; } }, request); }; - - public _addCountAggsToEsRequest = (request: ESRequest, _fieldToFilterOn?: string): ESRequest => { + public _addCountAggsToEsRequest = ( + request: ESRequest, + _fieldToFilterOn?: string + ): ESRequest => { return request; }; public _parseCountFromResponse = (_isUnfilteredQuery: boolean, _response: ESResponse): void => { return; }; - - } decorate(QueryStringFilter, { diff --git a/src/types.ts b/src/types.ts index fa3eef8..890b83c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -332,14 +332,10 @@ export type RawMultiSelectAggs = { * Query String Filter */ -export type QueryStringSubFieldFilterValue = { +export type QueryStringFieldFilter = { inclusion: 'include' | 'exclude'; kind?: 'should' | 'must'; - query_string: string; -}; - -export type QueryStringFieldFilter = { - [selectedValue: string]: QueryStringSubFieldFilterValue; + query: string; }; /** From e79d441f4e65c004aca27bd4ac7bf81bc025f138 Mon Sep 17 00:00:00 2001 From: Mark Rieth Date: Mon, 21 Dec 2020 11:29:47 -0800 Subject: [PATCH 4/6] Fix build errors --- src/filters/query_string_filter.ts | 6 +++--- src/manager.ts | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/filters/query_string_filter.ts b/src/filters/query_string_filter.ts index bd411c5..33d51fe 100644 --- a/src/filters/query_string_filter.ts +++ b/src/filters/query_string_filter.ts @@ -314,7 +314,7 @@ class QueryStringFilter extends BaseFilter< if (filter) { const query = filter.query; - const inclusion = filter.inclusion ?? config.defaultFilterInclusion; + const inclusion = filter.inclusion || config.defaultFilterInclusion; // See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html const newFilter = @@ -327,9 +327,9 @@ class QueryStringFilter extends BaseFilter< } } }; - const kindForSelectedValue = filter.kind ?? kind; + const kindForSelectedValue = filter.kind || kind; const existingFiltersForKind = - esRequest.query.bool[kindForSelectedValue as FilterKind] ?? []; + esRequest.query.bool[kindForSelectedValue as FilterKind] || []; return { ...esRequest, diff --git a/src/manager.ts b/src/manager.ts index 5f985a2..dea30d3 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -30,6 +30,7 @@ import Timeout from 'await-timeout'; import chunk from 'lodash.chunk'; import {PrefixSuggestion, FuzzySuggestion, BaseSuggestion} from './suggestions'; import Sort from './sort'; +import QueryStringFilter from './filters/query_string_filter'; /** * How the naming works: @@ -81,7 +82,8 @@ const DEFAULT_MANAGER_OPTIONS: Omit< range: new RangeFilter(), dateRange: new DateRangeFilter(), geo: new GeoFilter(), - terms: new TermsFilter() + terms: new TermsFilter(), + queryString: new QueryStringFilter() }, suggestions: { fuzzy: new FuzzySuggestion(), From 30673734e0d84b503b50d650acccc00c2b436d5d Mon Sep 17 00:00:00 2001 From: Mark Rieth Date: Mon, 21 Dec 2020 11:31:03 -0800 Subject: [PATCH 5/6] Bump minor package version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3060f80..ebaabb1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "elastic-composer", - "version": "4.3.1", + "version": "4.4.0", "description": "", "main": "dist/index.cjs.js", "module": "dist/index.es.js", From 42d300ead655c1bd133b0e058d166349060cf377 Mon Sep 17 00:00:00 2001 From: Mark Rieth Date: Mon, 21 Dec 2020 11:52:09 -0800 Subject: [PATCH 6/6] Make queryString field optional in IFiltersOptions --- src/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.ts b/src/types.ts index 890b83c..102e50d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -270,7 +270,7 @@ export interface IFiltersOptions { exists?: ExistsFilter; geo?: GeoFilter; terms?: TermsFilter; - queryString: QueryStringFilter + queryString?: QueryStringFilter; [customFilter: string]: BaseFilter | undefined; } @@ -288,7 +288,7 @@ export interface IFilters { exists: ExistsFilter; geo: GeoFilter; terms: TermsFilter; - queryString: QueryStringFilter + queryString: QueryStringFilter; [customFilter: string]: BaseFilter; }