{"id":14981091,"url":"https://github.com/social-native/elastic-composer","last_synced_at":"2025-10-29T04:31:46.594Z","repository":{"id":49243332,"uuid":"233910621","full_name":"social-native/elastic-composer","owner":"social-native","description":"Client-side Elasticsearch query generator and executor. Filter fields, find search suggestions, and paginate query results for your indicies using a simple, reactive, and high-level API","archived":false,"fork":false,"pushed_at":"2023-03-14T08:00:36.000Z","size":585,"stargazers_count":14,"open_issues_count":44,"forks_count":1,"subscribers_count":8,"default_branch":"master","last_synced_at":"2023-09-19T21:10:19.341Z","etag":null,"topics":["api","builder","composition","elasticsearch","filter","frontend","high-level-api","mobx","query","react","reactive","suggestion","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/social-native.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-14T18:41:27.000Z","updated_at":"2024-07-29T22:55:02.598Z","dependencies_parsed_at":"2024-07-29T22:54:52.209Z","dependency_job_id":"c4f0e3cc-cbcf-4e1c-91e6-092473ee0391","html_url":"https://github.com/social-native/elastic-composer","commit_stats":null,"previous_names":[],"tags_count":39,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/social-native%2Felastic-composer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/social-native%2Felastic-composer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/social-native%2Felastic-composer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/social-native%2Felastic-composer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/social-native","download_url":"https://codeload.github.com/social-native/elastic-composer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238768422,"owners_count":19527197,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","builder","composition","elasticsearch","filter","frontend","high-level-api","mobx","query","react","reactive","suggestion","typescript"],"created_at":"2024-09-24T14:02:54.416Z","updated_at":"2025-10-29T04:31:41.254Z","avatar_url":"https://github.com/social-native.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# elastic-composer\n\nA high-level Elasticsearch query manager and executor. Filter fields, find search suggestions, and paginate query results for your indicies. Comes with addons for persisting and rehydrationg filter state from localStorage and the URL. Batteries included for optionally initializing via index introspection. Fully configurable. Very delightful. Try a slice 🍰!\n\nExample:\n\n```typescript\nconst client = new AxiosESClient('my_url/my_index');\nconst crm = new Manager(client);\n\n// set filters on the elasticsearch index fields 'age', 'isMarried', 'id', 'tags', and 'user_profile.location'\ncrm.filter.range.setFilter('age', {greaterThan: 20, lessThanEqual: 60})\ncrm.filter.boolean.setFilter('isMarried', {state: true})\ncrm.filter.exists.setFilter('id')\ncrm.filter.multiselect.setFilter('tags', { isHuman: { inclusion: 'include' }, hasBlueHair: { inclusion: 'exclude' }})\ncrm.filters.geo.addToFilter('user_profile.location', 'my_location_search', {\n    inclusion: 'include',\n    kind: 'should',\n    points : [\n        {\"lat\" : 40, \"lon\" : -70},\n        {\"lat\" : 30, \"lon\" : -80},\n        {\"lat\" : 20, \"lon\" : -90}\n    ]\n})\n\nautorun(() =\u003e {\n  console.log(crm.results) // results of the above compound query\n})\n```\n\n\u003e Note: Internally all filter changes create `sideEffectRequests` that are put onto a processing queue. The above example will create 5 requests - one for each `setFilter` | `addFilter` action. However, because there is built in debouncing, if these actions occur within the debounce window, only the last request (fully compounded with all filters applied) will be executed.\n\nExample with React:\n\n```typescript\nexport default observer(() =\u003e {\n    const crm = useContext(Context.crm);\n    return (\n      \u003cdiv\u003e\n        \u003cdiv onClick={() =\u003e crm.filter.exists.setFilter('id')}/\u003e\n        \u003cdiv onClick={() =\u003e crm.filter.exists.clearFilter('id')}/\u003e\n        \u003cdiv\u003e\n          {crm.results}\n        \u003c/div\u003e\n      \u003c/div\u003e\n    )\n})\n```\n\n- [elastic-composer](#elastic-composer)\n  - [Install](#install)\n  - [Peer dependencies](#peer-dependencies)\n  - [About](#about)\n    - [Paradigm](#paradigm)\n    - [Available filters and suggestions](#available-filters-and-suggestions)\n    - [Enabling filters and suggestions](#enabling-filters-and-suggestions)\n    - [How filters and suggestions affect one another](#how-filters-and-suggestions-affect-one-another)\n    - [Extending and customizing filters](#extending-and-customizing-filters)\n  - [Quick Examples](#quick-examples)\n    - [Instantiate a manager](#instantiate-a-manager)\n    - [Instantiate a manager with specific config options for a range filter](#instantiate-a-manager-with-specific-config-options-for-a-range-filter)\n    - [Setting the fieldNameModifier for all fields in a filter](#setting-the-fieldnamemodifier-for-all-fields-in-a-filter)\n    - [Add a custom filter during manager instantiation](#add-a-custom-filter-during-manager-instantiation)\n    - [Add a custom suggestion during manager instantiation](#add-a-custom-suggestion-during-manager-instantiation)\n    - [Adding a custom client to the manager](#adding-a-custom-client-to-the-manager)\n    - [Set middleware](#set-middleware)\n    - [Get the initial results for a manager](#get-the-initial-results-for-a-manager)\n    - [Run a custom elasticsearch query using the current filters](#run-a-custom-elasticsearch-query-using-the-current-filters)\n    - [Get the raw elasticsearch query derived from the current filters](#get-the-raw-elasticsearch-query-derived-from-the-current-filters)\n    - [Setting a range filter](#setting-a-range-filter)\n    - [Setting a boolean filter](#setting-a-boolean-filter)\n    - [Setting a exists filter](#setting-a-exists-filter)\n    - [Setting a multi-select filter](#setting-a-multi-select-filter)\n    - [Setting a geo filter](#setting-a-geo-filter)\n    - [Clearing a single selection from a multi-select filter](#clearing-a-single-selection-from-a-multi-select-filter)\n    - [Clearing a filter](#clearing-a-filter)\n    - [Setting a prefix suggestion](#setting-a-prefix-suggestion)\n    - [Setting a fuzzy suggestion](#setting-a-fuzzy-suggestion)\n    - [Access suggestion results](#access-suggestion-results)\n    - [Access the results of a query](#access-the-results-of-a-query)\n    - [Access the raw response object of the current query](#access-the-raw-response-object-of-the-current-query)\n    - [Paginating through the results set](#paginating-through-the-results-set)\n    - [Checking if there is another page to paginate to](#checking-if-there-is-another-page-to-paginate-to)\n    - [Enabling aggregation data for a filter](#enabling-aggregation-data-for-a-filter)\n    - [Disabling aggregation data for a filter](#disabling-aggregation-data-for-a-filter)\n    - [Enabling suggestions](#enabling-suggestions)\n    - [Disabling suggestions](#disabling-suggestions)\n    - [Setting filter 'should' or 'must' kind](#setting-filter-should-or-must-kind)\n    - [Clearing all filters](#clearing-all-filters)\n    - [Clearing all suggestions](#clearing-all-suggestions)\n    - [Looking at all active suggestions](#looking-at-all-active-suggestions)\n    - [Looking at all active filters](#looking-at-all-active-filters)\n    - [Looking at all the Filter and Suggestion instances available for a filed](#looking-at-all-the-filter-and-suggestion-instances-available-for-a-filed)\n    - [Using the history API](#using-the-history-api)\n  - [API](#api)\n    - [Manager](#manager)\n      - [Initialization](#initialization)\n        - [Client](#client)\n        - [Options](#options)\n      - [Methods](#methods)\n      - [Attributes](#attributes)\n    - [Common Among All Filters](#common-among-all-filters)\n      - [Initialization](#initialization-1)\n      - [Methods](#methods-1)\n      - [Attributes](#attributes-1)\n    - [Boolean Specific](#boolean-specific)\n      - [Initialization](#initialization-2)\n        - [defaultConfig](#defaultconfig)\n        - [specificConfig](#specificconfig)\n      - [Methods](#methods-2)\n      - [Attributes](#attributes-2)\n    - [Range Specific](#range-specific)\n      - [Initialization](#initialization-3)\n        - [defaultConfig](#defaultconfig-1)\n        - [specificConfig](#specificconfig-1)\n      - [Methods](#methods-3)\n      - [Attributes](#attributes-3)\n    - [Exists Specific](#exists-specific)\n      - [Initialization](#initialization-4)\n        - [defaultConfig](#defaultconfig-2)\n        - [specificConfig](#specificconfig-2)\n      - [Methods](#methods-4)\n      - [Attributes](#attributes-4)\n    - [Multi-Select Specific](#multi-select-specific)\n      - [Initialization](#initialization-5)\n        - [defaultConfig](#defaultconfig-3)\n        - [specificConfig](#specificconfig-3)\n    - [Geo Specific](#geo-specific)\n      - [Initialization](#initialization-6)\n        - [defaultConfig](#defaultconfig-4)\n        - [specificConfig](#specificconfig-4)\n      - [Methods](#methods-5)\n      - [Attributes](#attributes-5)\n    - [Common Among All Suggestions](#common-among-all-suggestions)\n      - [Initialization](#initialization-7)\n      - [Methods](#methods-6)\n      - [Attributes](#attributes-6)\n    - [History API](#history-api)\n      - [Initialization](#initialization-8)\n      - [Methods](#methods-7)\n      - [Attributes](#attributes-7)\n  - [Verbose Examples](#verbose-examples)\n    - [Usage with React](#usage-with-react)\n  - [Extending Filters and Suggestions](#extending-filters-and-suggestions)\n\n\n## Install\n\n```\nnpm install --save elastic-composer\n```\n\n## Peer dependencies\n\nThis package requires that you also install:\n\n```typescript\n{\n        \"await-timeout\": \"^1.1.1\",\n        \"axios\": \"^0.19.1\", \u003c------- only needed if using the AxiosESClient\n        \"lodash.chunk\": \"^4.2.0\",\n        \"mobx\": \"^5.14.2\"\n        \"lodash.debounce\": \"^4.0.8\", \u003c------- only needed if using the History API\n        \"query-string\": \"^6.11.1\", \u003c------- only needed if using the History API\n        \"query-params-data\": \"^0.1.1\", \u003c------- only needed if using the History API\n}\n```\n\n## About\n\nThis library is a high level aid in querying an Elasticsearch index and building applications ontop of indexes. It is designed to bind directly to the view layer (React, Vue, Vanilla, etc..) of your app, and it handles the vast majority of associated business logic internally.\n\nThis library is written in MobX, which makes it reactive. If you don't want to use MobX, you can convert any attribute ([see all attributes in the API](#api)) to an observable stream (RxJS, 😎) using the [mobx-utils tool](https://github.com/mobxjs/mobx-utils#tostream).\n\n### Paradigm\n\n**TL;DR**: You describe how you want to filer each field via a `Filter API` customized to your index and the manager handles querying, state, and pagination.\n\nThe general paradigm is as follows:\n\nThere are 4 API's:\n\n - Filter API\n - Suggestion API\n - History API\n - Manager API\n\nThe flow is:\n\n1. You define all the fields of an ES index that you want to use via (A) configuration objects in either the Filter or Suggestion API or (B) introspection abilities in the Manager API. \n2. Once you have fields set that you can filter or find suggestions on, you use the Filter API to filter results and the Suggestion API to get suggestions (for parameters to use in filters - such as fuzzy or prefix search of values). \n3. The Manager API gives you access to results and allows you to paginate over the results. \n4. The History API records Filters and Suggestions that have been set, persists this state to the URL in a persistent store (like localStorage), and rehydrates from persisted state.\n\nEverything in this library is reactive. So once you set a filter, the manager will react to the change, and submit a new query to Elasticsearch using all the filters that have been set across all the fields. The manager handles debouncing, throttling and batching queries. \n\nIn addition to simply running new queries, Filters and Suggestions provide opinionated aggregates that help inform how well the filter is doing. For example, the Range Filter gives you aggregates that show you a histogram of documents with the filter applied and without it applied. By default, aggregates are turned off by default. The paradigm with aggregates is to turn them on when a user is accessing a UI element that allows seeing aggregate data and turn them off when the UI element is no long visible. Because aggregates will respond to all filter changes, if you don't turn them off when not in use, you will submit meaningless queries to Elasticsearch.\n\n### Available filters and suggestions\n\nThe currently available Filters are:\n\n-   `range`: Filter documents by fields that fit within a LT (\u003c), LTE(\u003c=), GT(\u003e), GTE(\u003e=) range\n-   `boolean`: Filter documents by fields that have a value of either `true` or `false`\n-   `exists`: Filter documents by fields that have any value existing for that field\n-   `multiselect`: Filter documents that have fields matching certain values (includes or excludes)\n-   `geo`: Filter documents that have fields with `geo_point` data in them\n\nThe currently available suggestions are:\n\n-   `prefix`: Get suggestions for fields based on matches with the same prefix\n-   `fuzzy`: Get suggestions for fields based on [fuzzy matching](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-fuzzy-query.html)\n\n### Enabling filters and suggestions\n\nAll Filters affect both the `query` and `aggs` part of an Elasticsearch request object. The `query` part is how the Filter impacts which documents match the filters. The `aggs` part provides information about how successful the filter is - showing things like histogram of range results, count of exists and not exists, etc... By default, the `aggs` part is disabled for every Filter. You should use `setAggsEnabledToTrue` and `setAggsEnabledToFalse` to toggle `aggs` for a Filter. The idea is to only run `aggs` queries when you want to show this data to the user.\n\nSimilarly, `Suggestions` are disabled by default. For the same reason above, suggestions shouldn't run unless you explicitly are showing suggestion data to a user. To toggle Suggestion enabled state use the methods `setEnabledToTrue` and `setEnabledToFalse`.\n\n### How filters and suggestions affect one another\n\nThe interplay between `Suggestions` and `Filters` is such:\n\n- `Suggestions` don't affect Filters, but they will react to every Filter change\n- `Filters` affect Suggestions and don't react to Suggestion changes\n\n### Extending and customizing filters\n\nExtending and overriding the set of usable Filters or Suggestions is both possible, and easy. See [Extending Filters and Suggestions](#extending-filters-and-suggestions) for a complete guide. The basic idea is that you extend a `base` Filter or `base` Suggestion and fill out methods that tell: (A) when the manager should react to changes, (B) how to mutate a Elasticsearch request object to add Filter or Suggestion specific `query` and `aggs`, (C) how to parse an Elasticsearch response object to extract `aggs`.\n\n## Quick Examples\n\nVarious use cases are described below. Be sure to check out the API for the full range of attributes and methods available on the Manager, Filters, and Suggestions.\n\n### Instantiate a manager\n\n```typescript\nimport {AxiosESClient, Manager} from 'elastic-composer';\n\n// instantiate an elasticsearch axios client made for this lib\nconst client = new AxiosESClient('my_url/my_index');\n\n// instantiate a manager\nconst manager = new Manager(client, {\n    pageSize: 100,\n    queryThrottleInMS: 350,\n    fieldBlackList: ['id']\n});\n```\n\n### Instantiate a manager with specific config options for a range filter\n\n```typescript\nimport {AxiosESClient, Manager, RangeFilter} from 'elastic-composer';\n\n// set the default config all filters will have if not explicitly set\n// by default we don't want aggs enabled unless we know the filter is being shown in the UI. So,\n// we use lifecycle methods in react to toggle this config attribute and set the default to `false`.\nconst defaultRangeFilterConfig = {\n    aggsEnabled: false,\n    defaultFilterKind: 'should',\n    getDistribution: true,\n    getRangeBounds: true,\n    rangeInterval: 1\n};\n\n// explicitly set the config for certain fields\nconst customRangeFilterConfig = {\n    age: {\n        field: 'user.age',\n        rangeInterval: 10\n    },\n    invites: {\n        field: 'user.invites',\n        getDistribution: false\n    }\n};\n\n// instantiate a range filter\nconst rangeFilter = new RangeFilter(defaultRangeFilterConfig, customRangeFilterConfig);\n\nconst options = {\n    pageSize: 100,\n    queryThrottleInMS: 350,\n    fieldBlackList: ['id'],\n    filters: {range: rangeFilter}\n};\n\nconst manager = new Manager(client, options);\n```\n\n### Setting the fieldNameModifier for all fields in a filter\n\nThe `fieldNameModifier` can be used to modify what the field name sent to Elasticsearch looks like. This is useful if you want to take a field name such as `tags` and turn it into `tags.keyword` for matching purposes.\n\nThe modifier is a function with the signature `(fieldName: string) =\u003e string`\n\n```typescript\nimport {AxiosESClient, Manager, MultiSelectFilter} from 'elastic-composer';\n\n// set the default config all filters will have if not explicitly set\n// by default we don't want aggs enabled unless we know the filter is being shown in the UI. So,\n// we use lifecycle methods in react to toggle this config attribute and set the default to `false`.\n\nconst defaultMultiSelectFilterConfig = {\n    defaultFilterKind: 'should',\n    defaultFilterInclusion: 'include',\n    defaultMatch: 'match',\n    getCount: true,\n    aggsEnabled: false,\n    fieldNameModifierQuery: (fieldName: string) =\u003e `${fieldName}`,\n    fieldNameModifierAggs: (fieldName: string) =\u003e `${fieldName}.keyword`\n};\n\n\n// instantiate a range filter\nconst multiselectFilter = new MultiSelectFilter(defaultMultiSelectFilterConfig);\n\nconst options = {\n    pageSize: 100,\n    queryThrottleInMS: 350,\n    fieldBlackList: ['id'],\n    filters: {multiselect: multiselectFilter}\n};\n\nconst manager = new Manager(client, options);\n```\n\n### Add a custom filter during manager instantiation\n\n```typescript\nimport MyCustomFilter from 'my_custom_filter';\nimport {AxiosESClient, Manager} from 'elastic-composer';\n\nconst client = new AxiosESClient('my_url/my_index');\nconst newCustomFilter = new MyCustomFilter();\n\nconst manager = new Manager(client, {\n    pageSize: 100,\n    queryThrottleInMS: 350,\n    fieldBlackList: ['id'],\n    filters: {myNewFilterName: newCustomFilter}\n});\n```\n\n### Add a custom suggestion during manager instantiation\n\n```typescript\nimport MyCustomSuggestion from 'my_custom_suggestion';\nimport {AxiosESClient, Manager} from 'elastic-composer';\n\nconst client = new AxiosESClient('my_url/my_index');\nconst newCustomSuggestion = new MyCustomSuggestion();\n\nconst manager = new Manager(client, {\n    pageSize: 100,\n    queryThrottleInMS: 350,\n    fieldBlackList: ['id'],\n    suggestions: {myNewSuggestionName: newCustomSuggestion}\n});\n```\n\n### Adding a custom client to the manager\n\nIf you don't have permissions set up on your Elasticsearch cluster, you will most likely want to create a custom client that uses your backend as a pass through layer for making Elasticsearch calls.\n\nAn example could look like this: \n\n```typescript\nimport {Manager, IClient, ESRequest, ESResponse, ESMappingType} from 'elastic-composer';\n\n/**\n * Create a custom client that works on a specific through backend graphql nodes\n * In this case, the client uses the nodes 'creatorCRMSearch' and 'creatorCRMFields'\n */\nclass CreatorIndexGQLClient\u003cSource extends object = object\u003e implements IClient {\n    public graphqlClient: GqlClient;\n\n    constructor(graphqlClient: GqlClient) {\n        if (graphqlClient === undefined) {\n            throw new Error(\n                'GraphqlQL client is undefined. Please instantiate this class with a GqlClient instance'\n            );\n        }\n        this.graphqlClient = graphqlClient;\n    }\n\n    public search = async (search: ESRequest): Promise\u003cESResponse\u003cSource\u003e\u003e =\u003e {\n        const {data} = await this.graphqlClient.client.query({\n            query: gql`\n                query CreatorCRMSearch($search: JSON) {\n                    creatorCRMSearch(search: $search)\n                }\n            `,\n            fetchPolicy: 'no-cache',\n            variables: {search: JSON.stringify(search)}\n        });\n        return JSON.parse(data.creatorCRMSearch);\n    };\n\n    public mapping = async (): Promise\u003cRecord\u003cstring, ESMappingType\u003e\u003e =\u003e {\n        const {data} = (await this.graphqlClient.client.query({\n            query: gql`\n                query CreatorCRMFields {\n                    creatorCRMFields\n                }\n            `,\n            fetchPolicy: 'no-cache'\n        })) as any;\n        return JSON.parse(data.creatorCRMFields);\n    };\n}\n\nconst customClient = new CreatorIndexGQLClient(gqlClient);\nconst creatorCRM = new Manager(customClient);\n```\n\n### Set middleware\n\n```typescript\nimport {Middleware} from 'elastic-composer';\n\nconst logRequestObj: Middleware = (\n    _effectRequest: EffectRequest\u003cEffectKinds\u003e,\n    request: ESRequest\n) =\u003e {\n    console.log(request);\n    return request;\n};\n\nmanager.setMiddleware([logRequestObj]);\n```\n\n### Get the initial results for a manager\n\nAll queries are treated as requests and added to an internal queue. Thus, you don't await this method but, react to the `manager.results` attribute.\n\n```typescript\nmanager.runStartQuery();\n```\n\n### Run a custom elasticsearch query using the current filters\n\nIf you wanted to bulk export a subset of the filtered results without having to paginate programmatically, you could request the results for a much larger page size this way over a reduced field list:\n\n```typescript\nconst results = await manager.runCustomFilterQuery({whiteList: ['id'], pageSize: 10000});\n```\n\n### Get the raw elasticsearch query derived from the current filters\n\n```typescript\nconst rawEsQuery = await manager.getCurrentEsQuery();\n```\n\n### Setting a range filter\n\n```typescript\nmanager.filters.range.setFilter('age', {greaterThanEqual: 20, lessThan: 40});\n```\n\n\u003e Note: This triggers a query to rerun with all the existing filters plus the range filter for `age` will be updated\n\u003e to only include people between the ages of 20-40 (inclusive to exclusive).\n\n### Setting a boolean filter\n\n```typescript\nmanager.filters.boolean.setFilter('isActive', {state: true});\n```\n\n### Setting a exists filter\n\nFor example, this will filter all documents so only the ones with a `facebook.id` are shown\n\n```typescript\nmanager.filters.boolean.setFilter('facebook.id', {exists: true});\n```\n\n### Setting a multi-select filter\n\nA multi select filter can be set in two ways: (1) all selections at once, or (2) one selection at a time.\n\nTo set all selections at once, you would do something like:\n\n```typescript\nmanager.filters.multiselect.setFilter('tags', {\n    is_good_user: {inclusion: 'include', match: 'match'},\n    has_green_hair: {inclusion: 'exclude', kind: 'must'},\n    likes_ham: {inclusion: 'include', kind: 'should', match: 'match_phrase'}\n});\n```\n\n\u003e Notice how `kind` is optional. If its not specified, it will default to whatever `defaultFilterKind` is set to for the filter (aka `manager.filter.multiselect.fieldConfigs['tags].defaultFilterKind`)\n\n\u003e Notice how `match` is optional. If its not specified, it will default to whatever `defaultMatch` is set to for the filter (aka `manager.filter.multiselect.fieldConfigs['tags].defaultMatch`)\n\nTo set one selection at a time, you would do:\n\n```typescript\nmanager.filters.multiselect.addToFilter('tags', 'has_green_hair', {\n    inclusion: 'exclude',\n    kind: 'must',\n    match: 'match_phrase'\n});\n```\n\n### Setting a geo filter\n\nGeo filters implement [geo bounding box, geo distance, and geo polygon](https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-queries.html) queries.\n\nLike a multiselect filter, you can add all filters at once for a field using `setFilter` or add them one by one using `addFilter`.\n\n```typescript\ncrm.filters.geo.addToFilter('user_profile.location', 'my_first_loc', {\n    'kind': 'should',\n    'inclusion': 'exclude',\n    'distance': '100mi',\n    'lat': 34.7850143,\n    'lon': -92.3912103\n})\n\ncrm.filters.geo.addToFilter('user_profile.location', 'my_second_loc', {\n    'kind': 'must',\n    'inclusion': 'include',\n    \"top_left\" : {\n        \"lat\" : 40.73,\n        \"lon\" : -74.1\n    },\n    \"bottom_right\" : {\n        \"lat\" : 40.01,\n        \"lon\" : -71.12\n    }\n})\n\ncrm.filters.geo.addToFilter('user_profile.location', 'my_third_loc', {\n    \"points\" : [\n        {\"lat\" : 40, \"lon\" : -70},\n        {\"lat\" : 30, \"lon\" : -80},\n        {\"lat\" : 20, \"lon\" : -90}\n    ]\n})\n```\n\n### Clearing a single selection from a multi-select filter\n\n```typescript\nmanager.filters.multiselect.removeFromFilter('tags', 'has_green_hair');\n```\n\n### Clearing a filter\n\nFor example, to clear the `isActive` field on a boolean filter, we would do:\n\n```typescript\nmanager.filters.boolean.clearFilter('tags');\n```\n\n### Setting a prefix suggestion\n\n```typescript\nmanager.suggestions.prefix.setSearch('tags', 'blu');\n```\n\n### Setting a fuzzy suggestion\n\n```typescript\nmanager.suggestions.fuzzy.setSearch('tags', 'ca');\n```\n\n### Access suggestion results\n\nAll suggestions have the same interface (currently). For both `prefix` and `fuzzy` would get the suggestions for a search like:\n\n```typescript\nmanager.suggestions.fuzzy.fieldSuggestions['tags'];\n\n// =\u003e [{ suggestion: 'car', count: 120}, { suggestion: 'can', count: 9 }]\n```\n\n### Access the results of a query\n\n```typescript\nmanager.results; // Array\u003cESHit\u003e\n```\n\nResults are an array where each object in the array has the type:\n\n```typescript\ntype ESHit\u003cSource extends object = object\u003e = {\n    _index: string;\n    _type: string;\n    _id: string;\n    _score: number;\n    _source: Source;\n    sort: ESRequestSortField;\n};\n```\n\n\u003e `_source` will be the document result from the index.\n\nThus, you would likely use the `results` like:\n\n```typescript\nmanager.results.map(r =\u003e r._source);\n```\n\n### Access the raw response object of the current query\n\n```typescript\nmanager.rawESResponse\n\n// =\u003e \n// {\"took\":1,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":2178389,\"max_score\":0.0,\"hits\":[]}}\n//\n```\n\n### Paginating through the results set\n\n```typescript\nmanager.nextPage();\nmanager.prevPage();\n\nmanager.currentPage; // number\n// # =\u003e 0 when no results exist\n// # =\u003e 1 for the first page of results\n```\n\n### Checking if there is another page to paginate to\n\n```typescript\nmanager.hasNextPage\n```\n\n### Enabling aggregation data for a filter\n\nBy default, aggregation data is turned off for all filter. This data shows things like count of exists field, histogram of range data, etc..\n\n```typescript\nmanager.filters.boolean.setAggsEnabledToTrue('tags');\n```\n\n\u003e The idea with enabling and disabling aggregation data is that these aggregations only need to run when a filter is visible to the user in the UI. Thus, enabling and disabling should mirror filter visibility in the UI.\n\n### Disabling aggregation data for a filter\n\n```typescript\nmanager.filters.boolean.setAggsEnabledToFalse('tags');\n```\n\n### Enabling suggestions\n\nSimilar to `filters`, suggestions are disabled by default because they rely on elasticsearch aggregations to run, and there is no point in collecting the data unless the user cares about it.\n\n```typescript\nmanager.suggestions.fuzzy.setEnabledToTrue('tags');\n```\n\n### Disabling suggestions\n\n```typescript\nmanager.suggestions.fuzzy.setEnabledToFalse('tags');\n```\n\n### Setting filter 'should' or 'must' kind\n\nAll filters can be use in `should` or `must` mode. By default, all filters are `should` filters unless explicitly changed to `must` filters. [Read this for more info on the difference between should and must](https://stackoverflow.com/questions/28768277/elasticsearch-difference-between-must-and-should-bool-query)\n\n```typescript\nmanager.filters.boolean.setKind('facebook.id', 'must');\n\n// or to go back to should:\n\nmanager.filters.boolean.setKind('facebook.id', 'should');\n```\n\n### Clearing all filters\n\n```typescript\nmanager.clearAllFilters()\n```\n\n### Clearing all suggestions\n\n```typescript\nmanager.clearAllSuggestions()\n```\n\n### Looking at all active suggestions\n\n```typescript\nmanager.activeSuggestions\n\n// =\u003e \n// { tags: [PrefixSuggestion, FuzzySuggestion], location: [PrefixSuggestion]}\n```\n\n### Looking at all active filters\n\n```typescript\nmanager.activeFilters\n\n// =\u003e \n// { tags: [MultiSelectFilter, ExistsFilter], location: [RangeFilter, ExistsFilter]}\n```\n\n### Looking at all the Filter and Suggestion instances available for a filed\n\n```typescript\nmanager.fieldsWithFiltersAndSuggestions\n\n// =\u003e\n// { tags: { filters: [MultiSelectFilter, ExistsFilter] suggestions: [PrefixSuggestion, FuzzySuggestion]} }\n```\n\n### Using the history API\n\n```typescript\nconst userHistory = new History(manager, 'user', { // set the url's query param key to `user`\n    historyPersister: localStorageHistoryPersister('user'), // set the local storage suffix key to `user`\n    historySize: 4\n});\n```\n\n## API\n\n### Manager\n\n#### Initialization\n\nThe manager constructor has the signature `(client, options) =\u003e ManagerInstance`\n\n##### Client\n\n`client` is an object than handles submitting query responses. It has the signature:\n\n```typescript\ninterface IClient\u003cSource extends object = object\u003e {\n    search: (request: ESRequest) =\u003e Promise\u003cESResponse\u003cSource\u003e\u003e;\n    mapping: () =\u003e Promise\u003cRecord\u003cstring, ESMappingType\u003e\u003e;\n    // With ESMappingType equal to https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html\n}\n```\n\nAt the moment there only exists an `AxiosESClient` client. This can be imported via a named import:\n\n```ts\nimport {AxiosESClient} from 'elastic-composer';\n\nconst axiosESClient = new AxiosESClient(endpoint);\n\n// endpoint is in the form: blah2lalkdjhgak.us-east-1.es.amazonaws.com/myindex1\n```\n\n##### Options\n\n`options` are used to configure the manager. There currently exist these options:\n\n```ts\ntype ManagerOptions = {\n    pageSize?: number;\n    queryThrottleInMS?: number;\n    fieldWhiteList?: string[];\n    fieldBlackList?: string[];\n    middleware?: Middleware[];\n    filters?: IFilters;\n    suggestions?: ISuggestions;\n};\n```\n\n-   `pageSize`: the number of results to expect when calling `manager.results`. The default size is 10.\n-   `queryThrottleInMS`: the amount of time to wait before executing an Elasticsearch query. The default time is 1000.\n-   `fieldWhiteList`: A list of elasticsearch fields that you only want to allow filtering on. This can't be used with `fieldBlackList`. Only white list fields will be returned in an elasticsearch query response.\n-   `fieldBlackList`: A list of elasticsearch fields that you don't want to allow filtering on. This can't be used with `fieldWhiteList`. Black list fields will be excluded from an elasticsearch query response.\n-   `middleware`: An array of custom middleware to run during elasticsearch request object construction. See below for the type.\n-   `filters`: An object of filter instances. Default filters will be instantiate if none are specified in this options field. This options field however can be used to override existing filters or specify a custom one.\n-   `suggestions`: An object of suggestion instances. Default suggestions will be instantiated if none are specified in this options field. This options field however can be used to override existing suggestions or specify a custom one.\n\nThe middleware function type signature is:\n\n```typescript\nMiddleware = (effectRequest: EffectRequest\u003cEffectKinds\u003e, request: ESRequest) =\u003e ESRequest;\n```\n\nExample of overriding the range filter:\n\n```ts\nconst options = {filters: {range: rangeFilterInstance}};\n```\n\nExample of overriding the fuzzy suggestion:\n\n```ts\nconst options = {suggestions: {fuzzy: fuzzyFilterInstance}};\n```\n\n#### Methods\n\n| method                | description                                                                                                                                                                                       | type                                                                                                                                      |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |\n| nextPage              | paginates forward                                                                                                                                                                                 | `(): void`                                                                                                                                |\n| prevPage              | paginates backward                                                                                                                                                                                | `(): void`                                                                                                                                |\n| clearAllFilters | clears all active filters | `(): void` | \n| clearAllSuggestions | clears all active suggestions | `(): void` |\n| getFieldNamesAndTypes | runs an introspection query on the index mapping and generates an object of elasticsearch fields and the filter type they correspond to                                                           | `async (): void`                                                                                                                          |\n| runStartQuery         | runs the initial elasticsearch query that fetches unfiltered data                                                                                                                                 | `(): void`                                                                                                                                |\n| runCustomFilterQuery  | runs a custom query using the existing applied filters outside the side effect queue flow. white lists and black lists control which data is returned in the elasticsearch response source object | `async (options?: {fieldBlackList?: string[], fieldWhiteList?: string[], pageSize?: number }): Promise\u003cESResponse\u003e`                       |\n| setMiddleware         | adds middleware to run during construction of the elasticsearch query request object                                                                                                              | `(middlewares: Middleware): void`. Middleware has the type `(effectRequest: EffectRequest\u003cEffectKinds\u003e, request: ESRequest) =\u003e ESRequest` |\n| getCurrentEsQuery        | gets the raw Elasicsearch query that is derived from the current state  | `() =\u003e ESRequest` |\n\n#### Attributes\n\n| attribute               | description                                                                                                                                   | notes                                                                               |\n| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| isSideEffectRunning     | a flag telling if a query is running                                                                                                          | `boolean`                                                                           |\n| currentPage             | the page number                                                                                                                               | `0` if there are no results. `1` for the first page. etc...                         |\n| fieldWhiteList          | the white list of fields that filters can exist for                                                                                           |                                                                                     |\n| fieldBlackList          | the black list of fields that filters can not exist for                                                                                       |                                                                                     |\n| pageSize                | the page size                                                                                                                                 | The default size is 10. This can be changed by setting manager options during init. |\n| queryThrottleInMS       | the throttle time for queries                                                                                                                 | The default is 1000 ms. This can be changed by setting manager options during init. |\n| filters                 | the filter instances that the manager controls                                                                                                |\n| indexFieldNamesAndTypes | A list of fields that can be filtered over and the filter name that this field uses. This is populated by the method `getFieldNamesAndTypes`. |\n| results | the results of the most recent query | The `results` type is Array\u003cESHit\u003e. See [the `results` quick example doc for the type](https://github.com/social-native/elastic-composer#access-the-results-of-a-query) |\n| rawESResponse | The response object from the client from the query | `ESResponse` |\n| activeSuggestions | the object of fields with active suggestions | `{ fieldName: SuggestionInstance[] }`  |\n| activeFilters | the object of fields with active filters | `{ fieldName: FilterInstance[] }`  |\n| fieldsWithFiltersAndSuggestions | the object of fields and the filters and suggestions that are available for them | `{ fieldName: { suggestions: SuggestionInstance[], filters: FilterInstance[] } }`  |\n| hasNextPage | Whether another page is available via the `nextPage` method | `boolean` |\n### Common Among All Filters\n\n#### Initialization\n\nAll filter constructors have the signature `(defaultConfig, specificConfig) =\u003e FilterTypeInstance`\n\n`defaultConfig` and `specificConfig` are specific to each filter class type.\n\n#### Methods\n\n| method                | description                                     | type                                                                             |\n| --------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------- |\n| setFilter             | sets the filter for a field                     | `(field: \u003cname of field\u003e, filter: \u003cfilter specific to filter class type\u003e): void` |\n| clearFilter           | clears the filter for a field                   | `(field: \u003cname of field\u003e): void`                                                 |\n| setKind               | sets the kind for a field                       | `(field: \u003cname of field\u003e, kind: should or must): void`                           |\n| setAggsEnabledToTrue  | enables fetching of aggs for this filter field  | `(field: \u003cname of field\u003e): void`                                                 |\n| setAggsEnabledToFalse | disables fetching of aggs for this filter field | `(field: \u003cname of field\u003e): void`                                                 |\n\n#### Attributes\n\n| attribute    | description                                                  | type                                                              |\n| ------------ | ------------------------------------------------------------ | ----------------------------------------------------------------- |\n| fieldConfigs | the config for a field, keyed by field name                  | `{ [\u003cnames of fields\u003e]: \u003cconfig specific to filter class type\u003e }` |\n| fieldFilters | the filters for a field, keyed by field name                 | `{ [\u003cnames of fields\u003e]: Filter }`                                 |\n| fieldKinds   | the kind (`should or must`) for a field, keyed by field name | `{ [\u003cnames of fields\u003e]: 'should' or 'must' }`                     |\n\n### Boolean Specific\n\n#### Initialization\n\nThe boolean constructor has the signature `(defaultConfig, specificConfig) =\u003e BooleanFilterInstance`\n\n##### defaultConfig\n\nThe configuration that each field will acquire if an override is not specifically set in `specificConfig`\n\n```typescript\ntype DefaultConfig = {\n    defaultFilterKind: 'should',\n    getCount: true,\n    aggsEnabled: false\n};\n```\n\n##### specificConfig\n\nThe explicit configuration set on a per field level. If a config isn't specified or only partially specified for a field, the defaultConfig will be used to fill in the gaps.\n\n```typescript\ntype SpecificConfig = Record\u003cstring, BooleanConfig\u003e;\n\ntype BooleanConfig = {\n    field: string;\n    defaultFilterKind?: 'should' | 'must';\n    getCount?: boolean;\n    aggsEnabled?: boolean;\n};\n```\n\n#### Methods\n\n| method    | description                 | type                                                                     |\n| --------- | --------------------------- | ------------------------------------------------------------------------ |\n| setFilter | sets the filter for a field | `(field: \u003cname of boolean field\u003e, filter: {state: true or false}): void` |\n\n#### Attributes\n\n| attribute       | description                                                                  | type                                                               |\n| --------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------ |\n| filteredCount   | the count of boolean values of all filtered documents, keyed by field name   | `{ [\u003cnames of boolean fields\u003e]: { true: number; false: number;} }` |\n| unfilteredCount | the count of boolean values of all unfiltered documents, keyed by field name | `{ [\u003cnames of boolean fields\u003e]: { true: number; false: number;} }` |\n\n### Range Specific\n\n#### Initialization\n\nThe range constructor has the signature `(defaultConfig, specificConfig) =\u003e RangeFilterInstance`\n\n##### defaultConfig\n\nThe configuration that each field will acquire if an override is not specifically set in `specificConfig`\n\n```typescript\ntype RangeConfig = {\n    defaultFilterKind: 'should',\n    getDistribution: true,\n    getRangeBounds: true,\n    rangeInterval: 1,\n    aggsEnabled: false\n};\n```\n\n##### specificConfig\n\nThe explicit configuration set on a per field level. If a config isn't specified or only partially specified for a field, the defaultConfig will be used to fill in the gaps.\n\n```typescript\ntype SpecificConfig = Record\u003cstring, RangeConfig\u003e;\n\ntype RangeConfig = {\n    field: string;\n    defaultFilterKind?: 'should' | 'must';\n    getDistribution?: boolean;\n    getRangeBounds?: boolean;\n    rangeInterval?: number;\n    aggsEnabled?: boolean;\n};\n```\n\n#### Methods\n\n| method    | description                 | type                                                                                                                                        |\n| --------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |\n| setFilter | sets the filter for a field | `(field: \u003cname of range field\u003e, filter: {lessThan?: number, greaterThan?: number, lessThanEqual?: number, greaterThanEqual?: number): void` |\n\n#### Attributes\n\n| attribute              | description                                                            | type                                                                                                                                      |\n| ---------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |\n| filteredRangeBounds    | the bounds of all filtered ranges (ex: 20 - 75), keyed by field name   | `{ [\u003cnames of range fields\u003e]: { min: { value: number; value_as_string?: string; }; max: { value: number; value_as_string?: string; };} }` |\n| unfilteredRangeBounds  | the bounds of all unfiltered ranges (ex: 0 - 100), keyed by field name | `{ [\u003cnames of range fields\u003e]: { min: { value: number; value_as_string?: string; }; max: { value: number; value_as_string?: string; };} }` |\n| filteredDistribution   | the distribution of all filtered ranges, keyed by field name           | `{[\u003cnames of range fields\u003e]: Array\u003c{ key: number; doc_count: number; }\u003e}`                                                                 |\n| unfilteredDistribution | the distribution of all filtered ranges, keyed by field name           | `{[\u003cnames of range fields\u003e]: Array\u003c{ key: number; doc_count: number; }\u003e}`                                                                 |\n\n### Exists Specific\n\n#### Initialization\n\nThe exists constructor has the signature `(defaultConfig, specificConfig) =\u003e ExistsFilterInstance`\n\n##### defaultConfig\n\nThe configuration that each field will acquire if an override is not specifically set in `specificConfig`\n\n```typescript\ntype DefaultConfig = {\n   defaultFilterKind: 'should',\n   getCount: true,\n   aggsEnabled: false\n};\n```\n\n##### specificConfig\n\nThe explicit configuration set on a per field level. If a config isn't specified or only partially specified for a field, the defaultConfig will be used to fill in the gaps.\n\n```typescript\ntype SpecificConfig = Record\u003cstring, ExistsConfig\u003e;\n\ntype ExistsConfig = {\n    field: string;\n    defaultFilterKind?: 'should' | 'must';\n    getCount?: boolean;\n    aggsEnabled?: boolean;\n};\n```\n\n#### Methods\n\n| method    | description                 | type                                                                     |\n| --------- | --------------------------- | ------------------------------------------------------------------------ |\n| setFilter | sets the filter for a field | `(field: \u003cname of exists field\u003e, filter: {exists: true or false}): void` |\n\n#### Attributes\n\n| attribute       | description                                                                 | type                                                                      |\n| --------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| filteredCount   | the count of exists values of all filtered documents, keyed by field name   | `{ [\u003cnames of exists fields\u003e]: { exists: number; doesntExist: number;} }` |\n| unfilteredCount | the count of exists values of all unfiltered documents, keyed by field name | `{ [\u003cnames of exists fields\u003e]: { exists: number; doesntExist: number;} }` |\n\n### Multi-Select Specific\n\n#### Initialization\n\nThe multiselect constructor has the signature `(defaultConfig, specificConfig) =\u003e MultiSelectFilterInstance`\n\n##### defaultConfig\n\nThe configuration that each field will acquire if an override is not specifically set in `specificConfig`\n\n```typescript\ntype DefaultConfig = {\n    defaultFilterKind: 'should',\n    defaultFilterInclusion: 'include',\n    defaultMatch: 'match',\n    getCount: true,\n    aggsEnabled: false,\n    fieldNameModifierQuery: (fieldName: string) =\u003e fieldName\n    fieldNameModifierAggs: (fieldName: string) =\u003e fieldName\n};\n```\n\n##### specificConfig\n\nThe explicit configuration set on a per field level. If a config isn't specified or only partially specified for a field, the defaultConfig will be used to fill in the gaps.\n\n```typescript\ntype SpecificConfig = Record\u003cstring, MultiSelectConfig\u003e;\n\ntype MultiSelectConfig = {\n    field: string;\n    defaultFilterKind?: 'should' | 'must';\n    defaultFilterInclusion?: 'include' | 'exclude';\n    defaultMatch?: 'match' | 'match_phrase';\n    getCount?: boolean;\n    aggsEnabled?: boolean;\n    fieldNameModifierQuery?: (fieldName: string) =\u003e string\n    fieldNameModifierAggs?: (fieldName: string) =\u003e string\n};\n```\n\n\u003e NOTE: fieldNameModifier functions will be overwritten to `(fieldName: string) =\u003e ${fieldName}.keyword` if match_phrase is specified as match.\n\n### Geo Specific\n\n\u003e NOTE: Geo filters do not have any aggs enabled! Do not try to use aggs with geo filters.\n\nExamples of GeoFilter actions and the queries they generate can be found at [src/filters/geo_filter_README.md](src/filters/geo_filter_README.md)\n\n#### Initialization\n\nThe geoFilter constructor has the signature `(defaultConfig, specificConfig) =\u003e GeoFilterInstance`\n\n##### defaultConfig\n\nThe configuration that each field will acquire if an override is not specifically set in `specificConfig`\n\n```typescript\ntype DefaultConfig = {\n    defaultFilterKind: 'should',\n    defaultFilterInclusion: 'include',\n    getCount: true,\n    aggsEnabled: false,\n    fieldNameModifierQuery: (fieldName: string) =\u003e fieldName\n    fieldNameModifierAggs: (fieldName: string) =\u003e fieldName\n};\n```\n\n##### specificConfig\n\nThe explicit configuration set on a per field level. If a config isn't specified or only partially specified for a field, the defaultConfig will be used to fill in the gaps.\n\n```typescript\ntype SpecificConfig = Record\u003cstring, GeoConfig\u003e;\n\ntype GeoConfig = {\n    field: string;\n    defaultFilterKind?: 'should' | 'must';\n    defaultFilterInclusion?: 'include' | 'exclude';\n    getCount?: boolean;\n    aggsEnabled?: boolean;\n    fieldNameModifierQuery?: (fieldName: string) =\u003e string\n    fieldNameModifierAggs?: (fieldName: string) =\u003e string\n};\n```\n\n#### Methods\n\nA filter selection has the type:\n\n```typescript\n{\n  inclusion: 'include' | 'exclude';\n  kind?: 'should' | 'must';\n}\n```\n\n| method           | description                              | type                                                                                                                                    |\n| ---------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |\n| setFilter        | sets the filter for a field              | `(field: \u003cname of geo field\u003e, filter: {[geoSubFilterReferenceName]: {inclusion: 'include' or 'exclude', kind?: 'should' or 'must'}}): void` |\n| addToFilter      | adds a single selection to a filter      | `addToFilter(field: \u003cname of geo field\u003e, geoSubFilterReferenceName: string, selectionFilter: {inclusion: 'include' or 'exclude', kind?: 'should' or 'must'}): void`          |\n| removeFromFilter | removes a single selection from a filter | `removeFromFilter(field: \u003cname of geo field\u003e, geoSubFilterReferenceName: string): void`                                                     |\n\n#### Attributes\n\n| attribute       | description                                                                      | type                                                                                |\n| --------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |\n\n\u003e Note: No aggregates are implemented, thus there are no attributes specific to this filter type.\n\n### Common Among All Suggestions\n\nThe suggestions that ship with this package all have the same public interface (for the moment). Thus, you can rely on this section for API documentation on each suggestion type.\n\n#### Initialization\n\nAll filter constructors have the signature `(defaultConfig, specificConfig) =\u003e SuggestionTypeInstance`\n\n`defaultConfig` and `specificConfig` are specific to each suggestion class type.\n\nThe `defaultConfig` looks like:\n\n```typescript\n{\n    defaultSuggestionKind: 'should',\n    enabled: false,\n    fieldNameModifierQuery: (fieldName: string) =\u003e fieldName,\n    fieldNameModifierAggs: (fieldName: string) =\u003e fieldName\n}\n```\n\nThe typings for the specific config object looks like:\n\n```typescript\n{\n    field: string;\n    defaultSuggestionKind?: 'should' | 'must';\n    enabled?: boolean;\n    fieldNameModifierQuery?: FieldNameModifier;\n    fieldNameModifierAggs?: FieldNameModifier;\n}\n```\n\n#### Methods\n\n| method            | description                                                | type                                                   |\n| ----------------- | ---------------------------------------------------------- | ------------------------------------------------------ |\n| setSearch         | sets the search term for a field to get suggestions for    | `(field: \u003cname of field\u003e, searchTerm: string): void`   |\n| clearSearch       | clears the search for a field                              | `(field: \u003cname of field\u003e): void`                       |\n| setKind           | sets the kind for a field                                  | `(field: \u003cname of field\u003e, kind: should or must): void` |\n| setEnabledToTrue  | enables fetching of suggestions for this suggestion field  | `(field: \u003cname of field\u003e): void`                       |\n| setEnabledToFalse | disables fetching of suggestions for this suggestion field | `(field: \u003cname of field\u003e): void`                       |\n\n#### Attributes\n\n| attribute        | description                                                  | type                                                                  |\n| ---------------- | ------------------------------------------------------------ | --------------------------------------------------------------------- |\n| fieldConfigs     | the config for a field, keyed by field name                  | `{ [\u003cnames of fields\u003e]: \u003cconfig specific to filter class type\u003e }`     |\n| fieldSuggestions | the suggestions for a field, keyed by field name             | `{ [\u003cnames of fields\u003e]: Array\u003c{suggestion: string; count: number}\u003e }` |\n| fieldSearches    | the searches for a field, keyed by field name                | `{ [\u003cnames of fields\u003e]: string }`                                     |\n| fieldKinds       | the kind (`should or must`) for a field, keyed by field name | `{ [\u003cnames of fields\u003e]: 'should' or 'must' }`                         |\n\n### History API\n\nThe history API allows you to:\n\n- record history of user interactions with filters and suggestions\n- allow you to serialize the current state to a URL query param\n- allow you to save the history to local storage and rehydrate from this storage.\n\n\n#### Initialization\n\nAll filter constructors have the signature `(manager: Manager, queryParamKey: string, options?: IHistoryOptions\u003cHistoryLocation\u003e) =\u003e SuggestionTypeInstance`\n\n\nThe `options` looks like:\n\n```typescript\n{\n    historySize?: number;\n    currentLocationStore?: UrlStore\u003cState\u003e;\n    historyPersister?: IHistoryPersister;\n    rehydrateOnStart?: boolean; // whether to run the `rehydrate` method in the constructor\n}\n```\n\nIn turn, the historyPersister has the type:\n\n```typescript\nIHistoryPersister {\n    setHistory: (location: Array\u003cHistoryLocation | undefined\u003e) =\u003e void;\n    getHistory: () =\u003e HistoryLocation[];\n}\n\n```\n\n#### Methods\n\n| method            | description                                                | type                                                   |\n| ----------------- | ---------------------------------------------------------- | ------------------------------------------------------ |\n| setCurrentState         | sets the current state of filters and suggestgions    | `(location: HistoryLocation): void`   |\n| back       | goes back in the history                              | `(): void`                       |\n| forward           | goes forward in the history                                | `(): void` |\n| rehydrate | rehydrates from URL or persistent storage | `(): void` |\n\n#### Attributes\n\n| attribute        | description                                                  | type                                                                  |\n| ---------------- | ------------------------------------------------------------ | --------------------------------------------------------------------- |\n| history     | the recorded history                  | `Array\u003cHistoryLocation | undefined\u003e`     |\n| currentLocationInHistoryCursor | the location in history, changed by going 'back' or 'forward'             | `number` |\n| hasRehydratedLocation | flag to tell if any location was rehydrated from when the `rehydrate` method was called |\n\n## Verbose Examples\n\nSee [./dev/app/](./dev/app/) for examples used in the development environment.\n\n### Usage with React\n\n```typescript\nimport {AxiosESClient, Manager} from 'elastic-composer';\n\nconst client = new AxiosESClient(process.env.ELASTIC_SEARCH_ENDPOINT);\nconst creatorCRM = new Manager(client);\n\ncreatorCRM.getFieldNamesAndTypes().then(() =\u003e {\n    creatorCRM.runStartQuery();\n});\n\nexport default {\n    exampleForm: React.createContext(exampleFormInstance),\n    creatorCRM: React.createContext(creatorCRM)\n};\n```\n\n## Extending Filters and Suggestions\n\nTODO\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsocial-native%2Felastic-composer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsocial-native%2Felastic-composer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsocial-native%2Felastic-composer/lists"}