odatafy-mongodb
Version:
convert oData requests through odatafy to MongoDB queries
328 lines (327 loc) • 13.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processNode = exports.generateMatchStage = exports.generateMatchFromFilterExpr = void 0;
var odatafy_parser_1 = require("odatafy-parser");
var mongodb_1 = require("mongodb");
/**
* TODO: Support geo functions
* TODO: date and time and totalseconds currently not supported - fun1arg
* TODO: totaloffset minutes not supported - func1arg
*/
/**
* Get a MongoDB match stage based on oData filter expression
* @param filterExpr oData filter expression
* @param opts options for generating the match stage
* @returns MongoDB match stage
*/
function generateMatchFromFilterExpr(filterExpr, opts) {
var ast = odatafy_parser_1.filterParser.parse(filterExpr);
return generateMatchStage(ast, opts);
}
exports.generateMatchFromFilterExpr = generateMatchFromFilterExpr;
/**
* Get a MongoDB skip stage based on the ast of a parsed oData filter expression
* @param ast abstract syntax tree of the filter expression
* @param opts options for generating the match stage
* @returns MongoDB match stage
*/
function generateMatchStage(ast, opts) {
return {
'$match': processNode(ast, undefined, opts)
};
}
exports.generateMatchStage = generateMatchStage;
function processNode(node, parentExpr, opts) {
if (!node) {
throw new Error('Something went wrong, node is undefined');
}
switch (node.nodeType) {
case odatafy_parser_1.NodeTypes.OperatorNode:
case undefined:
return processOperatorNode(node, parentExpr, opts);
case odatafy_parser_1.NodeTypes.ConstantNode:
return processConstantNode(node);
case odatafy_parser_1.NodeTypes.SymbolNode:
return processSymbolNode(node);
case odatafy_parser_1.NodeTypes.FuncNode0Args:
return processFuncNode0Args(node);
case odatafy_parser_1.NodeTypes.FuncNode1Args:
return processFuncNode1Args(node);
case odatafy_parser_1.NodeTypes.FuncNode2Args:
return processFuncNode2Args(node);
case odatafy_parser_1.NodeTypes.FuncNodeVarArgs:
return processFuncVarArgs(node);
default:
throw new Error("Unsupported NodeType: ".concat(node.nodeType));
}
}
exports.processNode = processNode;
function processFuncNode0Args(node) {
switch (node.func) {
case odatafy_parser_1.FuncNames0Args.Maxdatetime:
return new Date(8640000000000000);
case odatafy_parser_1.FuncNames0Args.Mindatetime:
return new Date(-8640000000000000);
case odatafy_parser_1.FuncNames0Args.Now:
return new Date();
}
}
function processFuncNode1Args(node) {
switch (node.func) {
case odatafy_parser_1.FuncNames1Args.Year:
return {
$year: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Month:
return {
$month: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Minute:
return {
$minute: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Second:
return {
$second: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Day:
return {
$dayOfMonth: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Hour:
return {
$hour: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Fractionalseconds:
return {
$millisecond: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Floor:
return {
$floor: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Ceiling:
return {
$ceil: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Length:
if (node.args[0].nodeType == odatafy_parser_1.NodeTypes.ConstantNode && node.args[0].type == odatafy_parser_1.ConstantNodeTypes.Array) {
return { $size: [processNode(node.args[0])] };
}
return {
$cond: {
if: { $isArray: [processNode(node.args[0])] },
then: { $size: [processNode(node.args[0])] },
else: { $strLenCP: processNode(node.args[0]) }
}
};
case odatafy_parser_1.FuncNames1Args.Toupper:
return {
$toUpper: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Tolower:
return {
$toLower: processNode(node.args[0])
};
case odatafy_parser_1.FuncNames1Args.Trim:
return {
$trim: {
input: processNode(node.args[0])
}
};
case odatafy_parser_1.FuncNames1Args.Round:
return {
$round: [processNode(node.args[0])]
};
default:
throw new Error("Function ".concat(node.func, " is not supported"));
}
}
//TODO: add functions with 2 args
function processFuncNode2Args(node) {
switch (node.func) {
case odatafy_parser_1.FuncNames2Args.Contains:
if (node.args[0].nodeType == odatafy_parser_1.NodeTypes.ConstantNode && node.args[0].type == odatafy_parser_1.ConstantNodeTypes.String) {
return {
$regexMatch: {
input: processNode(node.args[0]),
regex: processNode(node.args[1]).toString()
}
};
}
return {
$cond: {
if: { $isArray: [processNode(node.args[0])] },
then: {
$in: [processNode(node.args[1]), processNode(node.args[0])]
},
else: {
$regexMatch: {
input: processNode(node.args[0]),
regex: processNode(node.args[1]).toString()
}
}
}
};
case odatafy_parser_1.FuncNames2Args.MatchesPattern:
return {
$regexMatch: {
input: processNode(node.args[0]),
regex: processNode(node.args[1]).toString()
}
};
case odatafy_parser_1.FuncNames2Args.Startswith:
return {
$regexMatch: {
input: processNode(node.args[0]),
regex: "^".concat(processNode(node.args[1]).toString())
}
};
case odatafy_parser_1.FuncNames2Args.Endswith:
return {
$regexMatch: {
input: processNode(node.args[0]),
regex: "".concat(processNode(node.args[1]).toString(), "$")
}
};
case odatafy_parser_1.FuncNames2Args.Indexof:
case odatafy_parser_1.FuncNames2Args.Hassubsequence:
case odatafy_parser_1.FuncNames2Args.Hassubset:
case odatafy_parser_1.FuncNames2Args.Concat:
if ((node.args[0].nodeType == odatafy_parser_1.NodeTypes.ConstantNode && node.args[0].type == odatafy_parser_1.ConstantNodeTypes.String) &&
(node.args[1].nodeType == odatafy_parser_1.NodeTypes.ConstantNode && node.args[1].type == odatafy_parser_1.ConstantNodeTypes.String)) {
return {
$concat: [processNode(node.args[0]), processNode(node.args[1])]
};
}
else if ((node.args[0].nodeType == odatafy_parser_1.NodeTypes.ConstantNode && node.args[0].type == odatafy_parser_1.ConstantNodeTypes.Array) &&
(node.args[1].nodeType == odatafy_parser_1.NodeTypes.ConstantNode && node.args[1].type == odatafy_parser_1.ConstantNodeTypes.Array)) {
return {
$concatArrays: [processNode(node.args[0]), processNode(node.args[1])]
};
}
return {
"$switch": {
"branches": [
{
"case": { $and: [{ $isArray: [processNode(node.args[0])] }, { $isArray: [processNode(node.args[1])] }] },
"then": { $concatArrays: [processNode(node.args[0]), processNode(node.args[1])] }
},
{
"case": { $and: [{ $eq: [{ $type: processNode(node.args[0]) }, 'string'] }, { $eq: [{ $type: processNode(node.args[1]) }, 'string'] }] },
"then": { $concat: [processNode(node.args[0]), processNode(node.args[1])] }
}
]
}
};
default:
throw new Error("Function ".concat(node.func, " is not supported"));
}
}
function processFuncVarArgs(node) {
switch (node.func) {
case odatafy_parser_1.FuncNamesVarArgs.Cast:
if (node.args.length == 2) {
if (node.args[0].nodeType == odatafy_parser_1.NodeTypes.ConstantNode &&
node.args[1].nodeType == odatafy_parser_1.NodeTypes.SymbolNode &&
node.args[1].value === 'ObjectId') {
return new mongodb_1.ObjectId(node.args[0].value);
}
}
throw new Error("Typings not supported yet");
default:
throw new Error("Function ".concat(node.func, " is not supported"));
}
}
function processOperatorNode(node, parentExpr, opts) {
var needsExpr = false;
var result = {};
switch (node.op) {
case odatafy_parser_1.OperatorNodeOperators.And:
result = {
'$and': [
processNode(node.left, parentExpr),
processNode(node.right, parentExpr)
]
};
break;
case odatafy_parser_1.OperatorNodeOperators.Or:
result = {
'$or': [
processNode(node.left, parentExpr),
processNode(node.right, parentExpr)
]
};
break;
case odatafy_parser_1.OperatorNodeOperators.Eq:
case odatafy_parser_1.OperatorNodeOperators.Ne:
case odatafy_parser_1.OperatorNodeOperators.Lt:
case odatafy_parser_1.OperatorNodeOperators.Gt:
case odatafy_parser_1.OperatorNodeOperators.Le:
case odatafy_parser_1.OperatorNodeOperators.Ge:
needsExpr = true;
var op = node.op;
if (op == odatafy_parser_1.OperatorNodeOperators.Le) {
op = 'lte';
}
if (op == odatafy_parser_1.OperatorNodeOperators.Ge) {
op = 'gte';
}
result["$".concat(op)] = [processNode(node.left, true), processNode(node.right, true)];
break;
case odatafy_parser_1.OperatorNodeOperators.Not:
needsExpr = true;
result = {
'$not': [
processNode(node.right, true)
]
};
break;
case odatafy_parser_1.OperatorNodeOperators.Add: //add
result = {
'$add': [processNode(node.left, true), processNode(node.right, true)]
};
break;
case odatafy_parser_1.OperatorNodeOperators.Mod: //mod
result = {
'$mod': [processNode(node.left, true), processNode(node.right, true)]
};
break;
case odatafy_parser_1.OperatorNodeOperators.Divby: //divide
result = {
'$divide': [processNode(node.left, true), processNode(node.right, true)]
};
break;
case odatafy_parser_1.OperatorNodeOperators.Sub: //subtract
result = {
'$subtract': [processNode(node.left, true), processNode(node.right, true)]
};
break;
case odatafy_parser_1.OperatorNodeOperators.Mul: //multiply
result = {
'$multiply': [processNode(node.left, true), processNode(node.right, true)]
};
break;
case odatafy_parser_1.OperatorNodeOperators.Div: //integer division
result = {
'$toInt': {
'$divide': [processNode(node.left, true), processNode(node.right, true)]
}
};
break;
default:
throw new Error("Unsupported operator: ".concat(node.op));
}
if (!parentExpr && needsExpr && !(opts === null || opts === void 0 ? void 0 : opts.withoutExpr)) {
return {
"$expr": result
};
}
return result;
}
function processConstantNode(node) {
return node.value;
}
function processSymbolNode(node) {
return "$".concat(node.value);
}