import * as queryDef from "./query_def"; export class ElasticQueryBuilder { timeField: string; esVersion: number; constructor(options) { this.timeField = options.timeField; this.esVersion = options.esVersion; } getRangeFilter() { var filter = {}; filter[this.timeField] = { gte: "$timeFrom", lte: "$timeTo", format: "epoch_millis" }; return filter; } buildTermsAgg(aggDef, queryNode, target) { var metricRef, metric, y; queryNode.terms = { field: aggDef.field }; if (!aggDef.settings) { return queryNode; } queryNode.terms.size = parseInt(aggDef.settings.size, 10) === 0 ? 500 : parseInt(aggDef.settings.size, 10); if (aggDef.settings.orderBy !== void 0) { queryNode.terms.order = {}; queryNode.terms.order[aggDef.settings.orderBy] = aggDef.settings.order; // if metric ref, look it up and add it to this agg level metricRef = parseInt(aggDef.settings.orderBy, 10); if (!isNaN(metricRef)) { for (y = 0; y < target.metrics.length; y++) { metric = target.metrics[y]; if (metric.id === aggDef.settings.orderBy) { queryNode.aggs = {}; queryNode.aggs[metric.id] = {}; queryNode.aggs[metric.id][metric.type] = { field: metric.field }; break; } } } } if (aggDef.settings.min_doc_count !== void 0) { queryNode.terms.min_doc_count = parseInt( aggDef.settings.min_doc_count, 10 ); } if (aggDef.settings.missing) { queryNode.terms.missing = aggDef.settings.missing; } return queryNode; } getDateHistogramAgg(aggDef) { var esAgg: any = {}; var settings = aggDef.settings || {}; esAgg.interval = settings.interval; esAgg.field = this.timeField; esAgg.min_doc_count = settings.min_doc_count || 0; esAgg.extended_bounds = { min: "$timeFrom", max: "$timeTo" }; esAgg.format = "epoch_millis"; if (esAgg.interval === "auto") { esAgg.interval = "$__interval"; } if (settings.missing) { esAgg.missing = settings.missing; } return esAgg; } getHistogramAgg(aggDef) { var esAgg: any = {}; var settings = aggDef.settings || {}; esAgg.interval = settings.interval; esAgg.field = aggDef.field; esAgg.min_doc_count = settings.min_doc_count || 0; if (settings.missing) { esAgg.missing = settings.missing; } return esAgg; } getFiltersAgg(aggDef) { var filterObj = {}; for (var i = 0; i < aggDef.settings.filters.length; i++) { var query = aggDef.settings.filters[i].query; var label = aggDef.settings.filters[i].label; label = label === "" || label === undefined ? query : label; filterObj[label] = { query_string: { query: query, analyze_wildcard: true } }; } return filterObj; } documentQuery(query, size) { query.size = size; query.sort = {}; query.sort[this.timeField] = { order: "desc", unmapped_type: "boolean" }; // fields field not supported on ES 5.x if (this.esVersion < 5) { query.fields = ["*", "_source"]; } query.script_fields = {}; if (this.esVersion < 5) { query.fielddata_fields = [this.timeField]; } else { query.docvalue_fields = [this.timeField]; } return query; } addAdhocFilters(query, adhocFilters) { if (!adhocFilters) { return; } var i, filter, condition, queryCondition; for (i = 0; i < adhocFilters.length; i++) { filter = adhocFilters[i]; condition = {}; condition[filter.key] = filter.value; queryCondition = {}; queryCondition[filter.key] = { query: filter.value }; switch (filter.operator) { case "=": if (!query.query.bool.must) { query.query.bool.must = []; } query.query.bool.must.push({ match_phrase: queryCondition }); break; case "!=": if (!query.query.bool.must_not) { query.query.bool.must_not = []; } query.query.bool.must_not.push({ match_phrase: queryCondition }); break; case "<": condition[filter.key] = { lt: filter.value }; query.query.bool.filter.push({ range: condition }); break; case ">": condition[filter.key] = { gt: filter.value }; query.query.bool.filter.push({ range: condition }); break; case "=~": query.query.bool.filter.push({ regexp: condition }); break; case "!~": query.query.bool.filter.push({ bool: { must_not: { regexp: condition } } }); break; } } } build(target, adhocFilters?, queryString?) { // make sure query has defaults; target.metrics = target.metrics || [{ type: "count", id: "1" }]; target.bucketAggs = target.bucketAggs || [ { type: "date_histogram", id: "2", settings: { interval: "auto" } } ]; target.timeField = this.timeField; var i, nestedAggs, metric; var query = { size: 0, query: { bool: { filter: [ { range: this.getRangeFilter() }, { query_string: { analyze_wildcard: true, query: queryString } } ] } } }; this.addAdhocFilters(query, adhocFilters); // handle document query if (target.bucketAggs.length === 0) { metric = target.metrics[0]; if (!metric || metric.type !== "raw_document") { throw { message: "Invalid query" }; } var size = (metric.settings && metric.settings.size) || 500; return this.documentQuery(query, size); } nestedAggs = query; for (i = 0; i < target.bucketAggs.length; i++) { var aggDef = target.bucketAggs[i]; var esAgg = {}; switch (aggDef.type) { case "date_histogram": { esAgg["date_histogram"] = this.getDateHistogramAgg(aggDef); break; } case "histogram": { esAgg["histogram"] = this.getHistogramAgg(aggDef); break; } case "filters": { esAgg["filters"] = { filters: this.getFiltersAgg(aggDef) }; break; } case "terms": { this.buildTermsAgg(aggDef, esAgg, target); break; } case "geohash_grid": { esAgg["geohash_grid"] = { field: aggDef.field, precision: aggDef.settings.precision }; break; } } nestedAggs.aggs = nestedAggs.aggs || {}; nestedAggs.aggs[aggDef.id] = esAgg; nestedAggs = esAgg; } nestedAggs.aggs = {}; for (i = 0; i < target.metrics.length; i++) { metric = target.metrics[i]; if (metric.type === "count") { continue; } var aggField = {}; var metricAgg = null; if (queryDef.isPipelineAgg(metric.type)) { if (metric.pipelineAgg && /^\d*$/.test(metric.pipelineAgg)) { metricAgg = { buckets_path: metric.pipelineAgg }; } else { continue; } } else { metricAgg = { field: metric.field }; } for (var prop in metric.settings) { if ( metric.settings.hasOwnProperty(prop) && metric.settings[prop] !== null ) { metricAgg[prop] = metric.settings[prop]; } } aggField[metric.type] = metricAgg; nestedAggs.aggs[metric.id] = aggField; } return query; } getTermsQuery(queryDef) { var query: any = { size: 0, query: { bool: { filter: [{ range: this.getRangeFilter() }] } } }; if (queryDef.query) { query.query.bool.filter.push({ query_string: { analyze_wildcard: true, query: queryDef.query } }); } var size = 500; if (queryDef.size) { size = queryDef.size; } query.aggs = { "1": { terms: { field: queryDef.field, size: size, order: { _term: "asc" } } } }; return query; } }