plywood
Version:
A query planner and executor
299 lines (298 loc) • 13.2 kB
JavaScript
import { CastExpression, ConcatExpression, CustomTransformExpression, ExtractExpression, FallbackExpression, LengthExpression, LiteralExpression, LookupExpression, NumberBucketExpression, OverlapExpression, r, RefExpression, SubstrExpression, TimeBucketExpression, TimeFloorExpression, TimePartExpression, TransformCaseExpression, } from '../../expressions';
var DruidExtractionFnBuilder = (function () {
function DruidExtractionFnBuilder(options) {
this.customTransforms = options.customTransforms;
}
DruidExtractionFnBuilder.composeFns = function (f, g) {
if (!f || !g)
return f || g;
return {
type: 'cascade',
extractionFns: [].concat(f.type === 'cascade' ? f.extractionFns : f, g.type === 'cascade' ? g.extractionFns : g),
};
};
DruidExtractionFnBuilder.getLastFn = function (fn) {
if (fn && fn.type === 'cascade') {
var extractionFns = fn.extractionFns;
return extractionFns[extractionFns.length - 1];
}
else {
return fn;
}
};
DruidExtractionFnBuilder.prototype.expressionToExtractionFn = function (expression) {
var freeReferences = expression.getFreeReferences();
if (freeReferences.length > 1) {
throw new Error("must have at most 1 reference (has ".concat(freeReferences.length, "): ").concat(expression));
}
if (expression instanceof LiteralExpression) {
return this.literalToExtractionFn(expression);
}
else if (expression instanceof RefExpression) {
return this.refToExtractionFn(expression);
}
else if (expression instanceof ConcatExpression) {
return this.concatToExtractionFn(expression);
}
else if (expression instanceof CustomTransformExpression) {
return this.customTransformToExtractionFn(expression);
}
else if (expression instanceof NumberBucketExpression) {
return this.numberBucketToExtractionFn(expression);
}
else if (expression instanceof SubstrExpression) {
return this.substrToExtractionFn(expression);
}
else if (expression instanceof TimeBucketExpression ||
expression instanceof TimeFloorExpression) {
return this.timeFloorToExtractionFn(expression);
}
else if (expression instanceof TimePartExpression) {
return this.timePartToExtractionFn(expression);
}
else if (expression instanceof TransformCaseExpression) {
return this.transformCaseToExtractionFn(expression);
}
else if (expression instanceof LengthExpression) {
return this.lengthToExtractionFn(expression);
}
else if (expression instanceof ExtractExpression) {
return this.extractToExtractionFn(expression);
}
else if (expression instanceof LookupExpression) {
return this.lookupToExtractionFn(expression);
}
else if (expression instanceof FallbackExpression) {
return this.fallbackToExtractionFn(expression);
}
else if (expression instanceof CastExpression) {
return this.castToExtractionFn(expression);
}
else if (expression instanceof OverlapExpression) {
return this.overlapToExtractionFn(expression);
}
else {
throw new Error("can not convert ".concat(expression, " to extractionFn"));
}
};
DruidExtractionFnBuilder.prototype.literalToExtractionFn = function (expression) {
return {
type: 'lookup',
retainMissingValue: false,
replaceMissingValueWith: expression.getLiteralValue(),
lookup: {
type: 'map',
map: {},
},
};
};
DruidExtractionFnBuilder.prototype.refToExtractionFn = function (expression) {
if (expression.type === 'BOOLEAN') {
return {
type: 'lookup',
lookup: {
type: 'map',
map: {
'0': 'false',
'1': 'true',
'false': 'false',
'true': 'true',
},
},
};
}
else {
return null;
}
};
DruidExtractionFnBuilder.prototype.concatToExtractionFn = function (expression) {
var innerExpression = null;
var format = expression
.getExpressionList()
.map(function (ex) {
if (ex instanceof LiteralExpression) {
return ex.value.replace(/%/g, '\\%');
}
if (innerExpression) {
throw new Error("can not have multiple expressions in concat '".concat(expression, "'"));
}
innerExpression = ex;
return '%s';
})
.join('');
if (!innerExpression)
throw new Error("invalid concat expression '".concat(expression, "'"));
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(innerExpression), {
type: 'stringFormat',
format: format,
nullHandling: 'returnNull',
});
};
DruidExtractionFnBuilder.prototype.timeFloorToExtractionFn = function (expression) {
var operand = expression.operand, duration = expression.duration;
var timezone = expression.getTimezone();
var myExtractionFn = {
type: 'timeFormat',
granularity: {
type: 'period',
period: duration.toString(),
timeZone: timezone.toString(),
},
format: "yyyy-MM-dd'T'HH:mm:ss'Z",
timeZone: 'Etc/UTC',
};
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), myExtractionFn);
};
DruidExtractionFnBuilder.prototype.timePartToExtractionFn = function (expression) {
var operand = expression.operand, part = expression.part;
var timezone = expression.getTimezone();
var myExtractionFn;
var format = DruidExtractionFnBuilder.TIME_PART_TO_FORMAT[part];
if (format) {
myExtractionFn = {
type: 'timeFormat',
format: format,
locale: 'en-US',
timeZone: timezone.toString(),
};
}
else {
throw new Error("can not part on ".concat(part));
}
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), myExtractionFn);
};
DruidExtractionFnBuilder.prototype.numberBucketToExtractionFn = function (expression) {
var operand = expression.operand, size = expression.size, offset = expression.offset;
var bucketExtractionFn = { type: 'bucket' };
if (size !== 1)
bucketExtractionFn.size = size;
if (offset !== 0)
bucketExtractionFn.offset = offset;
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), bucketExtractionFn);
};
DruidExtractionFnBuilder.prototype.substrToExtractionFn = function (expression) {
var operand = expression.operand, position = expression.position, len = expression.len;
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), {
type: 'substring',
index: position,
length: len,
});
};
DruidExtractionFnBuilder.prototype.transformCaseToExtractionFn = function (expression) {
var operand = expression.operand, transformType = expression.transformType;
var type = DruidExtractionFnBuilder.CASE_TO_DRUID[transformType];
if (!type)
throw new Error("unsupported case transformation '".concat(type, "'"));
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), {
type: type,
});
};
DruidExtractionFnBuilder.prototype.lengthToExtractionFn = function (expression) {
var operand = expression.operand;
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), {
type: 'strlen',
});
};
DruidExtractionFnBuilder.prototype.extractToExtractionFn = function (expression) {
var operand = expression.operand, regexp = expression.regexp;
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), {
type: 'regex',
expr: regexp,
replaceMissingValue: true,
});
};
DruidExtractionFnBuilder.prototype.lookupToExtractionFn = function (expression) {
var operand = expression.operand, lookupFn = expression.lookupFn;
var lookupExtractionFn = {
type: 'registeredLookup',
lookup: lookupFn,
};
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), lookupExtractionFn);
};
DruidExtractionFnBuilder.prototype.fallbackToExtractionFn = function (expression) {
var operand = expression.operand, fallback = expression.expression;
if (operand instanceof ExtractExpression) {
var extractExtractionFn = this.extractToExtractionFn(operand);
var extractExtractionFnLast = DruidExtractionFnBuilder.getLastFn(extractExtractionFn);
if (fallback.isOp('ref')) {
delete extractExtractionFnLast.replaceMissingValue;
}
else if (fallback.isOp('literal')) {
extractExtractionFnLast.replaceMissingValueWith = fallback.getLiteralValue();
}
else {
throw new Error("unsupported fallback: ".concat(expression));
}
return extractExtractionFn;
}
else if (operand instanceof LookupExpression) {
var lookupExtractionFn = this.lookupToExtractionFn(operand);
var lookupExtractionFnLast = DruidExtractionFnBuilder.getLastFn(lookupExtractionFn);
if (fallback.isOp('ref')) {
lookupExtractionFnLast.retainMissingValue = true;
}
else if (fallback.isOp('literal')) {
lookupExtractionFnLast.replaceMissingValueWith = fallback.getLiteralValue();
}
else {
throw new Error("unsupported fallback: ".concat(expression));
}
return lookupExtractionFn;
}
if (fallback instanceof LiteralExpression) {
throw new Error("cant handle direct fallback: ".concat(expression));
}
throw new Error("can not convert fallback ".concat(expression, " to extractionFn"));
};
DruidExtractionFnBuilder.prototype.customTransformToExtractionFn = function (customTransform) {
var operand = customTransform.operand, custom = customTransform.custom;
var customExtractionFn = this.customTransforms[custom];
if (!customExtractionFn)
throw new Error("could not find extraction function: '".concat(custom, "'"));
var extractionFn = customExtractionFn.extractionFn;
if (typeof extractionFn.type !== 'string')
throw new Error("must have type in custom extraction fn '".concat(custom, "'"));
try {
JSON.parse(JSON.stringify(customExtractionFn));
}
catch (e) {
throw new Error("must have JSON extraction Fn '".concat(custom, "'"));
}
return DruidExtractionFnBuilder.composeFns(this.expressionToExtractionFn(operand), extractionFn);
};
DruidExtractionFnBuilder.prototype.castToExtractionFn = function (cast) {
if (cast.outputType === 'TIME') {
throw new Error("can not convert cast ".concat(cast, " to extractionFn"));
}
return this.expressionToExtractionFn(cast.operand);
};
DruidExtractionFnBuilder.prototype.overlapToExtractionFn = function (expression) {
var freeReferences = expression.operand.getFreeReferences();
var rhsType = expression.expression.type;
if (freeReferences[0] === '__time' &&
expression.expression instanceof LiteralExpression &&
(rhsType === 'TIME_RANGE' || rhsType === 'SET/TIME_RANGE')) {
expression = expression.operand
.cast('NUMBER')
.overlap(r(expression.expression.getLiteralValue().changeToNumber()));
}
throw new Error("can not convert overlap ".concat(expression, " to extractionFn"));
};
DruidExtractionFnBuilder.CASE_TO_DRUID = {
upperCase: 'upper',
lowerCase: 'lower',
};
DruidExtractionFnBuilder.TIME_PART_TO_FORMAT = {
SECOND_OF_MINUTE: 's',
MINUTE_OF_HOUR: 'm',
HOUR_OF_DAY: 'H',
DAY_OF_WEEK: 'e',
DAY_OF_MONTH: 'd',
DAY_OF_YEAR: 'D',
WEEK_OF_YEAR: 'w',
MONTH_OF_YEAR: 'M',
YEAR: 'Y',
};
return DruidExtractionFnBuilder;
}());
export { DruidExtractionFnBuilder };