UNPKG

odatafy-mongodb

Version:

convert oData requests through odatafy to MongoDB queries

328 lines (327 loc) 13.2 kB
"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); }