UNPKG

plywood

Version:
412 lines (411 loc) 17.2 kB
import { isDate } from 'chronoshift'; import { NamedArray } from 'immutable-class'; import { NumberRange, Range, Set, TimeRange } from '../../datatypes'; import { AndExpression, ContainsExpression, Expression, IpMatchExpression, IpSearchExpression, IpStringifyExpression, IsExpression, LiteralExpression, MatchExpression, MvContainsExpression, MvOverlapExpression, NotExpression, OrExpression, OverlapExpression, r, RefExpression, } from '../../expressions'; import { DruidExpressionBuilder } from './druidExpressionBuilder'; import { DruidExtractionFnBuilder } from './druidExtractionFnBuilder'; var DruidFilterBuilder = (function () { function DruidFilterBuilder(options) { this.rawAttributes = options.rawAttributes; this.timeAttribute = options.timeAttribute; this.allowEternity = options.allowEternity; this.customTransforms = options.customTransforms; } DruidFilterBuilder.prototype.filterToDruid = function (filter) { var _this = this; if (!filter.canHaveType('BOOLEAN')) throw new Error("can not filter on ".concat(filter.type)); if (filter.equals(Expression.FALSE)) { return { intervals: [], filter: null, }; } else { var _a = filter.extractFromAnd(function (ex) { return ((ex instanceof IsExpression || ex instanceof OverlapExpression) && _this.isTimeRef(ex.operand) && ex.expression instanceof LiteralExpression); }), extract = _a.extract, rest = _a.rest; return { intervals: this.timeFilterToIntervals(extract), filter: this.timelessFilterToFilter(rest), }; } }; DruidFilterBuilder.prototype.timeFilterToIntervals = function (filter) { if (!filter.canHaveType('BOOLEAN')) throw new Error("can not filter on ".concat(filter.type)); if (filter instanceof LiteralExpression) { if (!filter.value) return []; if (!this.allowEternity) throw new Error('must filter on time unless the allowEternity flag is set'); return DruidFilterBuilder.TRUE_INTERVAL; } else if (filter instanceof IsExpression) { var lhs = filter.operand, rhs = filter.expression; if (lhs instanceof RefExpression && rhs instanceof LiteralExpression) { return this.valueToIntervals(rhs.value); } else { throw new Error("can not convert ".concat(filter, " to Druid interval")); } } else if (filter instanceof OverlapExpression) { var lhs = filter.operand, rhs = filter.expression; if (lhs instanceof RefExpression && rhs instanceof LiteralExpression) { return this.valueToIntervals(rhs.value); } else { throw new Error("can not convert ".concat(filter, " to Druid intervals")); } } else { throw new Error("can not convert ".concat(filter, " to Druid intervals")); } }; DruidFilterBuilder.prototype.timelessFilterToFilter = function (filter) { var _this = this; if (!filter.canHaveType('BOOLEAN')) throw new Error("can not filter on ".concat(filter.type)); if (filter instanceof RefExpression) { filter = filter.is(true); } if (filter instanceof LiteralExpression) { if (filter.value === true) { return null; } else { return { type: 'false' }; } } else if (filter instanceof NotExpression) { return { type: 'not', field: this.timelessFilterToFilter(filter.operand), }; } else if (filter instanceof AndExpression) { return { type: 'and', fields: filter.getExpressionList().map(function (p) { return _this.timelessFilterToFilter(p); }), }; } else if (filter instanceof OrExpression) { return { type: 'or', fields: filter.getExpressionList().map(function (p) { return _this.timelessFilterToFilter(p); }), }; } else if (filter instanceof IsExpression) { var lhs = filter.operand, rhs = filter.expression; if (rhs instanceof LiteralExpression) { if (Set.isSetType(rhs.type)) { return this.makeInFilter(lhs, rhs.value); } else { return this.makeSelectorFilter(lhs, rhs.value); } } else { throw new Error("can not convert ".concat(filter, " to Druid filter")); } } else if (filter instanceof OverlapExpression) { var lhs_1 = filter.operand, rhs = filter.expression; if (rhs instanceof LiteralExpression) { var rhsType = rhs.type; if (rhsType === 'SET/STRING' || rhsType === 'SET/NUMBER' || rhsType === 'SET/NULL') { return this.makeInFilter(lhs_1, rhs.value); } else if (Set.unwrapSetType(rhsType) === 'TIME_RANGE' && this.isTimeRef(lhs_1)) { return this.makeIntervalFilter(lhs_1, rhs.value); } else if (rhsType === 'NUMBER_RANGE' || rhsType === 'TIME_RANGE' || rhsType === 'STRING_RANGE') { return this.makeBoundFilter(lhs_1, rhs.value); } else if (rhsType === 'SET/NUMBER_RANGE' || rhsType === 'SET/TIME_RANGE' || rhsType === 'SET/STRING_RANGE') { return { type: 'or', fields: rhs.value.elements.map(function (range) { return _this.makeBoundFilter(lhs_1, range); }), }; } else { throw new Error("not supported OVERLAP rhs type ".concat(rhsType)); } } else { throw new Error("can not convert ".concat(filter, " to Druid filter")); } } else if (filter instanceof MatchExpression) { return this.makeRegexFilter(filter.operand, filter.regexp); } else if (filter instanceof ContainsExpression) { var lhs = filter.operand, rhs = filter.expression, compare = filter.compare; return this.makeContainsFilter(lhs, rhs, compare); } else if (filter instanceof MvContainsExpression) { var operand_1 = filter.operand, mvArray = filter.mvArray; return { type: 'and', fields: mvArray.map(function (elem) { return _this.makeSelectorFilter(operand_1, elem); }), }; } else if (filter instanceof MvOverlapExpression) { return this.makeInFilter(filter.operand, Set.fromJS(filter.mvArray)); } else if (filter instanceof IpMatchExpression) { return this.makeExpressionFilter(filter.operand.ipMatch(filter.ipToSearch.toString(), filter.ipSearchType)); } else if (filter instanceof IpSearchExpression) { return this.makeExpressionFilter(filter.operand.ipSearch(filter.ipToSearch.toString(), filter.ipSearchType)); } else if (filter instanceof IpStringifyExpression) { return this.makeExpressionFilter(filter.operand.ipStringify()); } throw new Error("could not convert filter ".concat(filter, " to Druid filter")); }; DruidFilterBuilder.prototype.valueToIntervals = function (value) { if (isDate(value)) { return TimeRange.intervalFromDate(value); } else if (value instanceof TimeRange) { return value.toInterval(); } else if (value instanceof Set) { return value.elements.map(function (v) { if (isDate(v)) { return TimeRange.intervalFromDate(v); } else if (v instanceof TimeRange) { return v.toInterval(); } else { throw new Error("can not convert set value ".concat(JSON.stringify(v), " to Druid interval")); } }); } else { throw new Error("can not convert ".concat(JSON.stringify(value), " to Druid intervals")); } }; DruidFilterBuilder.prototype.makeSelectorFilter = function (ex, value) { var attributeInfo = this.getSingleReferenceAttributeInfo(ex); if (!attributeInfo) { return this.makeExpressionFilter(ex.is(r(value))); } if (attributeInfo.unsplitable) { throw new Error("can not convert ".concat(ex, " = ").concat(value, " to filter because it references an un-filterable metric '").concat(attributeInfo.name, "' which is most likely rolled up.")); } var extractionFn; try { extractionFn = new DruidExtractionFnBuilder(this).expressionToExtractionFn(ex); } catch (_a) { return this.makeExpressionFilter(ex.is(r(value))); } if (value instanceof Range) value = value.start; var druidFilter = { type: 'selector', dimension: this.getDimensionNameForAttributeInfo(attributeInfo), value: value, }; if (extractionFn) druidFilter.extractionFn = extractionFn; return druidFilter; }; DruidFilterBuilder.prototype.makeInFilter = function (ex, valueSet) { var _this = this; var elements = valueSet.elements; var attributeInfo = this.getSingleReferenceAttributeInfo(ex); if (!attributeInfo) { var fields = elements.map(function (value) { return _this.makeSelectorFilter(ex, value); }); return { type: 'or', fields: fields }; } var extractionFn; try { extractionFn = new DruidExtractionFnBuilder(this).expressionToExtractionFn(ex); } catch (_a) { return this.makeExpressionFilter(ex.is(r(valueSet))); } var inFilter = { type: 'in', dimension: this.getDimensionNameForAttributeInfo(attributeInfo), values: elements, }; if (extractionFn) inFilter.extractionFn = extractionFn; return inFilter; }; DruidFilterBuilder.prototype.makeBoundFilter = function (ex, range) { var r0 = range.start; var r1 = range.end; var bounds = range.bounds; var attributeInfo = this.getSingleReferenceAttributeInfo(ex); if (!attributeInfo) { return this.makeExpressionFilter(ex.overlap(range)); } var extractionFn; try { extractionFn = new DruidExtractionFnBuilder(this).expressionToExtractionFn(ex); } catch (_a) { return this.makeExpressionFilter(ex.overlap(range)); } var boundFilter = { type: 'bound', dimension: this.getDimensionNameForAttributeInfo(attributeInfo), }; if (extractionFn) boundFilter.extractionFn = extractionFn; if (range instanceof NumberRange || attributeInfo.nativeType === 'LONG') { boundFilter.ordering = 'numeric'; } function dataToBound(d) { if (attributeInfo.nativeType === 'LONG') { return d.valueOf(); } else { return d.toISOString(); } } if (r0 != null) { boundFilter.lower = isDate(r0) ? dataToBound(r0) : r0; if (bounds[0] === '(') boundFilter.lowerStrict = true; } if (r1 != null) { boundFilter.upper = isDate(r1) ? dataToBound(r1) : r1; if (bounds[1] === ')') boundFilter.upperStrict = true; } return boundFilter; }; DruidFilterBuilder.prototype.makeIntervalFilter = function (ex, range) { var attributeInfo = this.getSingleReferenceAttributeInfo(ex); if (!attributeInfo) { return this.makeExpressionFilter(ex.overlap(range)); } var extractionFn; try { extractionFn = new DruidExtractionFnBuilder(this).expressionToExtractionFn(ex); } catch (_a) { return this.makeExpressionFilter(ex.overlap(range)); } var interval = this.valueToIntervals(range); var intervalFilter = { type: 'interval', dimension: this.getDimensionNameForAttributeInfo(attributeInfo), intervals: Array.isArray(interval) ? interval : [interval], }; if (extractionFn) intervalFilter.extractionFn = extractionFn; return intervalFilter; }; DruidFilterBuilder.prototype.makeRegexFilter = function (ex, regex) { var attributeInfo = this.getSingleReferenceAttributeInfo(ex); if (!attributeInfo) { return this.makeExpressionFilter(ex.match(regex)); } var extractionFn; try { extractionFn = new DruidExtractionFnBuilder(this).expressionToExtractionFn(ex); } catch (_a) { return this.makeExpressionFilter(ex.match(regex)); } var regexFilter = { type: 'regex', dimension: this.getDimensionNameForAttributeInfo(attributeInfo), pattern: regex, }; if (extractionFn) regexFilter.extractionFn = extractionFn; return regexFilter; }; DruidFilterBuilder.prototype.makeContainsFilter = function (lhs, rhs, compare) { if (rhs instanceof LiteralExpression) { var attributeInfo = this.getSingleReferenceAttributeInfo(lhs); if (!attributeInfo) { return this.makeExpressionFilter(lhs.contains(rhs, compare)); } if (lhs instanceof RefExpression && attributeInfo.termsDelegate) { return { type: 'fullText', textColumn: this.getDimensionNameForAttributeInfo(attributeInfo), termsColumn: attributeInfo.termsDelegate, query: rhs.value, matchAll: true, usePrefixForLastTerm: true, }; } var extractionFn = void 0; try { extractionFn = new DruidExtractionFnBuilder(this).expressionToExtractionFn(lhs); } catch (_a) { return this.makeExpressionFilter(lhs.contains(rhs, compare)); } var searchFilter = { type: 'search', dimension: this.getDimensionNameForAttributeInfo(attributeInfo), query: { type: 'contains', value: rhs.value, caseSensitive: compare === ContainsExpression.NORMAL, }, }; if (extractionFn) searchFilter.extractionFn = extractionFn; return searchFilter; } else { return this.makeExpressionFilter(lhs.contains(rhs, compare)); } }; DruidFilterBuilder.prototype.makeExpressionFilter = function (filter) { var druidExpression = new DruidExpressionBuilder(this).expressionToDruidExpression(filter); if (druidExpression === null) { throw new Error("could not convert ".concat(filter, " to Druid expression for filter")); } return { type: 'expression', expression: druidExpression, }; }; DruidFilterBuilder.prototype.getSingleReferenceAttributeInfo = function (ex) { var freeReferences = ex.getFreeReferences(); if (freeReferences.length !== 1) return null; var referenceName = freeReferences[0]; return this.getAttributesInfo(referenceName); }; DruidFilterBuilder.prototype.getDimensionNameForAttributeInfo = function (attributeInfo) { return attributeInfo.name === this.timeAttribute ? DruidFilterBuilder.TIME_ATTRIBUTE : attributeInfo.name; }; DruidFilterBuilder.prototype.getAttributesInfo = function (attributeName) { return NamedArray.get(this.rawAttributes, attributeName); }; DruidFilterBuilder.prototype.isTimeRef = function (ex) { return ex instanceof RefExpression && ex.name === this.timeAttribute; }; DruidFilterBuilder.TIME_ATTRIBUTE = '__time'; DruidFilterBuilder.TRUE_INTERVAL = '1000/3000'; return DruidFilterBuilder; }()); export { DruidFilterBuilder };