UNPKG

plywood

Version:
336 lines (335 loc) 17.5 kB
import { NamedArray } from 'immutable-class'; import { Set } from '../../datatypes'; import { AbsoluteExpression, AddExpression, AndExpression, CastExpression, ChainableExpression, ChainableUnaryExpression, ConcatExpression, ContainsExpression, DivideExpression, ExtractExpression, FallbackExpression, IndexOfExpression, IpMatchExpression, IpSearchExpression, IpStringifyExpression, IsExpression, LengthExpression, LiteralExpression, LogExpression, LookupExpression, MatchExpression, MultiplyExpression, MvContainsExpression, MvOverlapExpression, NotExpression, NumberBucketExpression, OrExpression, OverlapExpression, PowerExpression, RefExpression, SubstrExpression, SubtractExpression, ThenExpression, TimeBucketExpression, TimeFloorExpression, TimePartExpression, TimeShiftExpression, TransformCaseExpression, } from '../../expressions'; import { continuousFloorExpression } from '../../helper'; var DruidExpressionBuilder = (function () { function DruidExpressionBuilder(options) { this.rawAttributes = options.rawAttributes; this.timeAttribute = options.timeAttribute; } DruidExpressionBuilder.escape = function (str) { return str.replace(DruidExpressionBuilder.UNSAFE_CHAR, function (s) { return '\\u' + ('000' + s.charCodeAt(0).toString(16)).substr(-4); }); }; DruidExpressionBuilder.escapeVariable = function (name) { return "\"".concat(DruidExpressionBuilder.escape(name), "\""); }; DruidExpressionBuilder.escapeLiteral = function (x) { if (x == null) return 'null'; if (x.toISOString) { return String(x.valueOf()); } else if (typeof x === 'number') { return String(x); } else { return "'".concat(DruidExpressionBuilder.escape(String(x)), "'"); } }; DruidExpressionBuilder.escapeLike = function (str) { return str.replace(/([%_~])/g, '~$1'); }; DruidExpressionBuilder.expressionTypeToOutputType = function (type) { switch (type) { case 'TIME': case 'TIME_RANGE': return 'LONG'; case 'NUMBER': case 'NUMBER_RANGE': return 'DOUBLE'; default: return 'STRING'; } }; DruidExpressionBuilder.prototype.expressionToDruidExpression = function (expression) { var _this = this; if (expression instanceof LiteralExpression) { var literalValue = expression.getLiteralValue(); if (literalValue === null) { return "null"; } else { switch (typeof literalValue) { case 'string': return DruidExpressionBuilder.escapeLiteral(literalValue); case 'number': return String(literalValue); case 'boolean': return String(Number(literalValue)); case 'object': if (literalValue instanceof Date) { return DruidExpressionBuilder.escapeLiteral(literalValue); } else return "no_such_type"; default: return "no_such_type"; } } } else if (expression instanceof RefExpression) { if (expression.name === this.timeAttribute) { return '__time'; } else { var exStr = DruidExpressionBuilder.escapeVariable(expression.name); var info = this.getAttributesInfo(expression.name); if (info) { if (info.nativeType === 'STRING') { if (info.type === 'TIME') { exStr = this.castToType(exStr, info.nativeType, info.type); } } } return exStr; } } else if (expression instanceof ChainableExpression) { var myOperand = expression.operand; var ex1_1 = this.expressionToDruidExpression(myOperand); if (expression instanceof CastExpression) { return this.castToType(ex1_1, expression.operand.type, expression.outputType); } else if (expression instanceof SubstrExpression) { return "substring(".concat(ex1_1, ",").concat(expression.position, ",").concat(expression.len, ")"); } else if (expression instanceof ExtractExpression) { return "regexp_extract(".concat(ex1_1, ",").concat(DruidExpressionBuilder.escapeLiteral(expression.regexp), ",1)"); } else if (expression instanceof MatchExpression) { return "(regexp_extract(".concat(ex1_1, ",").concat(DruidExpressionBuilder.escapeLiteral(expression.regexp), ")!='')"); } else if (expression instanceof ContainsExpression) { var needle = expression.expression; if (needle instanceof LiteralExpression) { var needleValue = DruidExpressionBuilder.escape(DruidExpressionBuilder.escapeLike(needle.value)); if (expression.compare === ContainsExpression.IGNORE_CASE) { return "like(lower(".concat(ex1_1, "),'%").concat(needleValue.toLowerCase(), "%','~')"); } else { return "like(".concat(ex1_1, ",'%").concat(needleValue, "%','~')"); } } else { throw new Error("can not plan ".concat(expression, " into Druid")); } } else if (expression instanceof LengthExpression) { return "strlen(".concat(ex1_1, ")"); } else if (expression instanceof NotExpression) { return "!".concat(ex1_1); } else if (expression instanceof AbsoluteExpression) { return "abs(".concat(ex1_1, ")"); } else if (expression instanceof NumberBucketExpression) { return continuousFloorExpression(ex1_1, 'floor', expression.size, expression.offset); } else if (expression instanceof TimePartExpression) { var format = DruidExpressionBuilder.TIME_PART_TO_FORMAT[expression.part]; if (!format) throw new Error("can not convert ".concat(expression.part, " to Druid expression format")); return "timestamp_extract(".concat(ex1_1, ",'").concat(format, "',").concat(DruidExpressionBuilder.escapeLiteral(expression.timezone.toString()), ")"); } else if (expression instanceof TimeFloorExpression || expression instanceof TimeBucketExpression) { return "timestamp_floor(".concat(ex1_1, ",'").concat(expression.duration, "',null,").concat(DruidExpressionBuilder.escapeLiteral(expression.timezone.toString()), ")"); } else if (expression instanceof TimeShiftExpression) { return "timestamp_shift(".concat(ex1_1, ",'").concat(expression.duration, "',").concat(expression.step, ",").concat(DruidExpressionBuilder.escapeLiteral(expression.timezone.toString()), ")"); } else if (expression instanceof LookupExpression) { return "lookup(".concat(ex1_1, ",").concat(DruidExpressionBuilder.escapeLiteral(expression.lookupFn), ")"); } else if (expression instanceof TransformCaseExpression) { if (expression.transformType === TransformCaseExpression.UPPER_CASE) { return "upper(".concat(ex1_1, ")"); } else { return "lower(".concat(ex1_1, ")"); } } else if (expression instanceof MvContainsExpression) { return "array_contains(".concat(ex1_1, ", [").concat(expression.mvArray .map(DruidExpressionBuilder.escapeLiteral) .join(','), "])"); } else if (expression instanceof MvOverlapExpression) { return "array_overlap(".concat(ex1_1, ", [").concat(expression.mvArray .map(DruidExpressionBuilder.escapeLiteral) .join(','), "])"); } else if (expression instanceof IpMatchExpression) { return expression.ipSearchType === 'ipPrefix' ? "ip_match(".concat(DruidExpressionBuilder.escapeLiteral(expression.ipToSearch.toString()), ", ").concat(ex1_1, ")") : "ip_match(".concat(ex1_1, ", ").concat(DruidExpressionBuilder.escapeLiteral(expression.ipToSearch.toString()), ")"); } else if (expression instanceof IpSearchExpression) { return expression.ipSearchType === 'ipPrefix' ? "ip_search(".concat(DruidExpressionBuilder.escapeLiteral(expression.ipToSearch.toString()), ", ").concat(ex1_1, ")") : "ip_search(".concat(ex1_1, ", ").concat(DruidExpressionBuilder.escapeLiteral(expression.ipToSearch.toString()), ")"); } else if (expression instanceof IpStringifyExpression) { return "ip_stringify(".concat(ex1_1, ")"); } else if (expression instanceof ChainableUnaryExpression) { var myExpression = expression.expression; if (expression instanceof ConcatExpression) { return ('concat(' + expression .getExpressionList() .map(function (ex) { return _this.expressionToDruidExpression(ex); }) .join(',') + ')'); } var ex2 = this.expressionToDruidExpression(myExpression); if (expression instanceof AddExpression) { return "(".concat(ex1_1, "+").concat(ex2, ")"); } else if (expression instanceof SubtractExpression) { return "(".concat(ex1_1, "-").concat(ex2, ")"); } else if (expression instanceof MultiplyExpression) { return "(".concat(ex1_1, "*").concat(ex2, ")"); } else if (expression instanceof DivideExpression) { if (myExpression instanceof LiteralExpression) { return "(cast(".concat(ex1_1, ",'DOUBLE')/").concat(ex2, ")"); } else { return "if(".concat(ex2, "!=0,(cast(").concat(ex1_1, ",'DOUBLE')/").concat(ex2, "),null)"); } } else if (expression instanceof PowerExpression) { return "pow(".concat(ex1_1, ",").concat(ex2, ")"); } else if (expression instanceof LogExpression) { var myLiteral = myExpression.getLiteralValue(); if (myLiteral === Math.E) return "log(".concat(ex1_1, ")"); if (myLiteral === 10) return "log10(".concat(ex1_1, ")"); return "log(".concat(ex1_1, ")/log(").concat(ex2, ")"); } else if (expression instanceof ThenExpression) { return "if(".concat(ex1_1, ",").concat(ex2, ",null)"); } else if (expression instanceof FallbackExpression) { return "nvl(".concat(ex1_1, ",").concat(ex2, ")"); } else if (expression instanceof AndExpression) { return "(".concat(ex1_1, "&&").concat(ex2, ")"); } else if (expression instanceof OrExpression) { return "(".concat(ex1_1, "||").concat(ex2, ")"); } else if (expression instanceof IsExpression) { var myLiteral = myExpression.getLiteralValue(); if (myLiteral instanceof Set) { return ('(' + myLiteral.elements .map(function (e) { return "".concat(ex1_1, "==").concat(DruidExpressionBuilder.escapeLiteral(e)); }) .join('||') + ')'); } else { return "(".concat(ex1_1, "==").concat(ex2, ")"); } } else if (expression instanceof OverlapExpression) { var myExpressionType = myExpression.type; switch (myExpressionType) { case 'NUMBER_RANGE': case 'TIME_RANGE': if (myExpression instanceof LiteralExpression) { var range = myExpression.value; return this.overlapExpression(ex1_1, DruidExpressionBuilder.escapeLiteral(range.start), DruidExpressionBuilder.escapeLiteral(range.end), range.bounds); } throw new Error("can not convert ".concat(expression, " to Druid expression")); case 'STRING_RANGE': if (myExpression instanceof LiteralExpression) { var stringRange = myExpression.value; return this.overlapExpression(ex1_1, DruidExpressionBuilder.escapeLiteral(stringRange.start), DruidExpressionBuilder.escapeLiteral(stringRange.end), stringRange.bounds); } throw new Error("can not convert ".concat(expression, " to Druid expression")); case 'SET/NUMBER_RANGE': case 'SET/TIME_RANGE': if (myExpression instanceof LiteralExpression) { var setOfRange = myExpression.value; return setOfRange.elements .map(function (range) { return _this.overlapExpression(ex1_1, DruidExpressionBuilder.escapeLiteral(range.start), DruidExpressionBuilder.escapeLiteral(range.end), range.bounds); }) .join('||'); } throw new Error("can not convert ".concat(expression, " to Druid expression")); default: throw new Error("can not convert ".concat(expression, " to Druid expression")); } } else if (expression instanceof IndexOfExpression) { return "strpos(".concat(ex1_1, ",").concat(ex2, ")"); } } } throw new Error("can not convert ".concat(expression, " to Druid expression")); }; DruidExpressionBuilder.prototype.castToType = function (operand, sourceType, destType) { switch (destType) { case 'TIME': if (sourceType === 'NUMBER') { return "cast(".concat(operand, ",'LONG')"); } else { return "timestamp(".concat(operand, ")"); } case 'STRING': return "cast(".concat(operand, ",'STRING')"); case 'NUMBER': return "cast(".concat(operand, ",'DOUBLE')"); default: throw new Error("cast to ".concat(destType, " not implemented yet")); } }; DruidExpressionBuilder.prototype.overlapExpression = function (operand, start, end, bounds) { if (start === end && bounds === '[]') return "(".concat(operand, "==").concat(start, ")"); var startExpression = null; if (start !== 'null') { startExpression = start + (bounds[0] === '[' ? '<=' : '<') + operand; } var endExpression = null; if (end !== 'null') { endExpression = operand + (bounds[1] === ']' ? '<=' : '<') + end; } if (startExpression) { return endExpression ? "(".concat(startExpression, " && ").concat(endExpression, ")") : startExpression; } else { return endExpression ? endExpression : 'true'; } }; DruidExpressionBuilder.prototype.getAttributesInfo = function (attributeName) { return NamedArray.get(this.rawAttributes, attributeName); }; DruidExpressionBuilder.TIME_PART_TO_FORMAT = { SECOND_OF_MINUTE: 'SECOND', MINUTE_OF_HOUR: 'MINUTE', HOUR_OF_DAY: 'HOUR', DAY_OF_WEEK: 'DOW', DAY_OF_MONTH: 'DAY', DAY_OF_YEAR: 'DOY', WEEK_OF_YEAR: 'WEEK', MONTH_OF_YEAR: 'MONTH', QUARTER: 'QUARTER', YEAR: 'YEAR', }; DruidExpressionBuilder.UNSAFE_CHAR = /[^a-z0-9 ,._\-;:(){}\[\]<>!@#$%^&*`~?]/gi; return DruidExpressionBuilder; }()); export { DruidExpressionBuilder };