UNPKG

@asymmetrik/elastic-querybuilder

Version:
237 lines (204 loc) 7.09 kB
const BaseBuilder = require('./BaseBuilder'); const invariant = require('./invariant'); const { applyRawParameter, prepareFilteredAggregation } = require('./utils'); const { ERRORS, DEFAULTS } = require('./constants'); class QueryBuilder extends BaseBuilder { constructor (options = {}) { super(); this._query = { from: options.from || DEFAULTS.FROM, size: options.size || DEFAULTS.SIZE }; // Items for the build method this._raw = []; } /** * @description Update the from setting * @param {number} from - New value for from * @return {QueryBuilder} this */ from (from) { if (from !== undefined) { this._query.from = from; } return this; } /** * @description Update the size setting * @param {number} size - New value for size * @return {QueryBuilder} this */ size (size) { if (size !== undefined) { this._query.size = size; } return this; } /** * @description Sets the track scores * @param {number} trackScores - Boolean value for tracking score * @return {QueryBuilder} this */ trackScores (trackScores) { if (trackScores !== undefined) { this._query.track_scores = trackScores; } return this; } /** * @description Add a raw parameter to any part of your query * @param {string} path - The path to add the value at * @param {*} value - Value to add at the path * @return {BooleanQueryBuilder} this */ raw (path, value) { invariant(path && value !== undefined && arguments.length === 2, ERRORS.RAW); this._raw.push({ path, value }); return this; } /** * @description Clone the current builder * @return {QueryBuilder} this */ clone () { return Object.assign( Object.create(Object.getPrototypeOf(this)), JSON.parse(JSON.stringify(this)) ); } /** * @description Build our boolean ES query * @param {Object} options * @param {boolean} options.name - Name for your filtered aggregations, default is 'all' * @param {boolean} options.filterAggs - Whether of not to apply filters to an aggregation * @return An elasticsearch query */ build (options = {}) { // If should is combined with any other boolean query, it will only affect the score // and the query will only match the other bool options. See the 'should' section // here: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html const path = this.shouldUseFilter() ? 'query.bool.filter' : 'query'; applyRawParameter(this._query, path, super.build()); // Add filtered aggregations if we have any if (this.hasAggs() && options.filterAggs) { applyRawParameter(this._query, 'aggs', prepareFilteredAggregation( this.getAggs(), this.getQueries(), options.name )); } // Add aggregations if we have any else if (this.hasAggs()) { applyRawParameter(this._query, 'aggs', this.getAggs()); } // Add our sorting options if (this.hasSort()) { applyRawParameter(this._query, 'sort', this.getSorts()); } // finally add any raw parameter that may exist this._raw.forEach((param) => applyRawParameter(this._query, param.path, param.value)); return Object.assign({}, this._query); } /** * @description Build our DisMax ES query. You can pass anything into options if you want. * We are not filtering any options, but keep in mind, anything you pass needs to be a valid * property for Elasticsearch * @param {Object} options * @param {number} options.tie_breaker - tie breaker for ranking terms * @param {number} options.boost - Boost to apply to the query * @param {Array<Object>} options.queries - Array of queries to use with the builder * @return An elasticsearch query */ buildDisMax (options = {}) { invariant(Array.isArray(options.queries), ERRORS.NOT_AN_ARRAY); // Check if we have other queries to apply if (this.hasQuery()) { // Add the options to a boolean filter applyRawParameter(this._query, 'query.bool.filter', [ super.build(), { dis_max: options } ]); } // Add the options to our query else { applyRawParameter(this._query, 'query.dis_max', options); } // Add our sorting options if (this.hasSort()) { applyRawParameter(this._query, 'sort', this.getSorts()); } // finally add any raw parameter that may exist this._raw.forEach((param) => applyRawParameter(this._query, param.path, param.value)); return Object.assign({}, this._query); } /** * @description Build our MultiMatch ES query. You can pass anything into options if you want. * We are not filtering any options, but keep in mind, anything you pass needs to be a valid * property for Elasticsearch * @param {Object} options * @param {string} options.query - String to query for * @param {Array<string>} options.fields - Fields to apply this to * @param {string} options.type - type to use, default is most_fields * @param {number} options.tie_breaker - tie breaker for ranking terms * @param {string} options.minimum_should_match - Boost to apply to the query * @return An elasticsearch query */ buildMultiMatch (options = {}) { invariant(Array.isArray(options.fields) && options.query, ERRORS.MULTI_MATCH_ARGS); // Check if we have other queries to apply if (this.hasQuery()) { // Add the options to a boolean filter applyRawParameter(this._query, 'query.bool.filter', [ super.build(), { multi_match: options } ]); } // Add the options to our query else { applyRawParameter(this._query, 'query.multi_match', options); } // Add our sorting options if (this.hasSort()) { applyRawParameter(this._query, 'sort', this.getSorts()); } // finally add any raw parameter that may exist this._raw.forEach((param) => applyRawParameter(this._query, param.path, param.value)); return Object.assign({}, this._query); } /** * @description Build our function_score ES query * @param {Object} options * @param {boolean} options.name - Name for your filtered aggregations, default is 'all' * @param {boolean} options.filterAggs - Whether of not to apply filters to an aggregation * @return An elasticsearch query */ buildFunctionScore (options = {}) { // Apply all of our queries const queryPath = this.shouldUseFilter() ? 'query.function_score.query.bool.filter' : 'query.function_score.query'; applyRawParameter(this._query, queryPath, super.build()); // Apply any functions applyRawParameter(this._query, 'query.function_score.functions', this.getFuncs()); // Add filtered aggregations if we have any if (this.hasAggs() && options.filterAggs) { applyRawParameter(this._query, 'aggs', prepareFilteredAggregation( this.getAggs(), this.getQueries(), options.name )); } // Add aggregations if we have any else if (this.hasAggs()) { applyRawParameter(this._query, 'aggs', this.getAggs()); } // Add our sorting options if (this.hasSort()) { applyRawParameter(this._query, 'sort', this.getSorts()); } // Add any raw parameters this._raw.forEach((param) => applyRawParameter(this._query, param.path, param.value)); return Object.assign({}, this._query); } } module.exports = QueryBuilder;