Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Last active June 18, 2021 13:00
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomasdarimont/b54a8e9fcc01ed59d465 to your computer and use it in GitHub Desktop.
Save thomasdarimont/b54a8e9fcc01ed59d465 to your computer and use it in GitHub Desktop.
Simpler arithmetic expression syntax for MongoDB Aggregation Framework via AST Transformations
> expr("(a+b)*c")
{ "$multiply" : [ { "$add" : [ "$a", "$b" ] }, "$c" ] }
> expr("1+2*2+3")
{
"$add" : [
{
"$add" : [
1,
{
"$multiply" : [
2,
2
]
}
]
},
3
]
}
> expr("-(1+2*2+3)")
{
"$multiply" : [
-1,
{
"$add" : [
{
"$add" : [
1,
{
"$multiply" : [
2,
2
]
}
]
},
3
]
}
]
}
// use var args
> expr("(netPrice * _[0] + _[1]) * _[2]", 1.1 , 3.4, 1.19)
{
"$multiply" : [
{
"$add" : [
{
"$multiply" : [
"$netPrice",
1.1
]
},
3.4
]
},
1.19
]
}
// use wrap arguments in array:
> expr("(netPrice * _[0] + _[1]) * _[2]", [1.1 , 3.4, 1.19])
{
"$multiply" : [
{
"$add" : [
{
"$multiply" : [
"$netPrice",
1.1
]
},
3.4
]
},
1.19
]
}
db.exprExample.insert({a:1,b:2,c:3})
>db.exprExample.aggregate([{$project: {test: expr("a+b*c")}}])
{ "_id" : ObjectId("53b51a0c5fac782ffc8dcfce"), "test" : 7 }
>db.exprExample.aggregate([{$project: {test: expr("(a+b)*c")}}])
{ "_id" : ObjectId("53b51a0c5fac782ffc8dcfce"), "test" : 9 }
// http://jsep.from.so/
load("/Users/tom/Documents/dev/repos/soney/jsep/build/jsep.js")
//Based on the work we did for:
//https://spring.io/blog/2013/12/04/what-s-new-in-spring-data-mongodb-1-4-m1
function transformJsepExpression(node, args) {
var operators = {"+": "$add", "-": "$subtract", "*": "$multiply", "/": "$divide"};
switch (node.type) {
case "BinaryExpression":
var part = {};
part[operators[node.operator]] = [ transformJsepExpression(node.left, args), transformJsepExpression(node.right, args) ];
return part;
case "Literal":
return node.value;
case "Identifier":
return "$" + node.name;
case "UnaryExpression":
if (node.operator == "-" && node.prefix) {
//if(node.argument.type == "Literal"){
// we cannot simply return negative value since function result must be an object :(
// return -transformJsepExpression(node.argument);
//}
return {"$multiply": [-1, transformJsepExpression(node.argument, args)]};
} else if (node.operator == "+") {
return transformJsepExpression(node.argument, args);
}
case "MemberExpression":
if (node.object
&& node.object.type == "Identifier"
&& node.object.name == "_"
&& args
&& typeof node.property.value == "number"
&& node.property.value < args.length) {
return args[node.property.value];
}
}
return "error";
};
function expr(exprString, args) {
//treat first parameter as array or convert variable list of parameters to array and ignore first element (expression)
var argsList = typeof args == "object" ? args : Array.prototype.splice.call(arguments, 1, arguments.length - 1);
return transformJsepExpression(jsep(exprString), argsList);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment