Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
Copyright 2013 Red Hat, Inc. and/or its affiliates.
This file is part of lightblue.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package com.redhat.lightblue.mongo.crud.js;
import java.math.BigInteger;
import java.math.BigDecimal;
import java.util.Map;
import org.bson.FieldNameValidator;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.text.SimpleDateFormat;
import com.redhat.lightblue.metadata.EntityMetadata;
import com.redhat.lightblue.metadata.FieldTreeNode;
import com.redhat.lightblue.metadata.ArrayField;
import com.redhat.lightblue.metadata.Type;
import com.redhat.lightblue.metadata.types.*;
import com.redhat.lightblue.query.*;
import com.redhat.lightblue.util.Error;
import com.redhat.lightblue.util.MutablePath;
import com.redhat.lightblue.util.Path;
/**
* This class translates a query to javascript. It is used to
* translate nontrivial query expressions to be used under a $where
* construct
*/
public class JSQueryTranslator {
public static final String ERR_INVALID_COMPARISON = "mongo-translation:invalid-comparison";
public static final String ERR_INVALID_FIELD = "mongo-translation:invalid-field";
private final EntityMetadata md;
private static final SimpleDateFormat ISODATE_FORMAT=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
private static final Map BINARY_COMPARISON_OPERATOR_JS_MAP;
static{
BINARY_COMPARISON_OPERATOR_JS_MAP = new HashMap<>();
BINARY_COMPARISON_OPERATOR_JS_MAP.put(BinaryComparisonOperator._eq, "==");
BINARY_COMPARISON_OPERATOR_JS_MAP.put(BinaryComparisonOperator._neq, "!=");
BINARY_COMPARISON_OPERATOR_JS_MAP.put(BinaryComparisonOperator._lt, "<");
BINARY_COMPARISON_OPERATOR_JS_MAP.put(BinaryComparisonOperator._gt, ">");
BINARY_COMPARISON_OPERATOR_JS_MAP.put(BinaryComparisonOperator._lte, "<=");
BINARY_COMPARISON_OPERATOR_JS_MAP.put(BinaryComparisonOperator._gte, ">=");
}
public JSQueryTranslator(EntityMetadata md) {
this.md=md;
}
public Expression translateQuery(QueryExpression query) {
Context ctx=new Context(md.getFieldTreeRoot(),null);
ctx.topLevel=new Function();
ctx.topLevel.block=translateQuery(ctx,query);
return ctx.topLevel;
}
private Block translateQuery(Context context,QueryExpression query) {
Block ret=null;
if (query instanceof ArrayContainsExpression) {
ret = translateArrayContainsExpression(context, (ArrayContainsExpression) query);
} else if (query instanceof ArrayMatchExpression) {
ret = translateArrayElemMatch(context, (ArrayMatchExpression) query);
} else if (query instanceof FieldComparisonExpression) {
ret = translateFieldComparison(context, (FieldComparisonExpression) query);
} else if (query instanceof NaryLogicalExpression) {
ret = translateNaryLogicalExpression(context, (NaryLogicalExpression) query);
} else if (query instanceof NaryValueRelationalExpression) {
ret = translateNaryValueRelationalExpression(context, (NaryValueRelationalExpression) query);
} else if (query instanceof NaryFieldRelationalExpression) {
ret = translateNaryFieldRelationalExpression(context, (NaryFieldRelationalExpression) query);
} else if (query instanceof RegexMatchExpression) {
ret = translateRegexMatchExpression(context, (RegexMatchExpression) query);
} else if (query instanceof UnaryLogicalExpression) {
ret = translateUnaryLogicalExpression(context, (UnaryLogicalExpression) query);
} else {
ret = translateValueComparisonExpression(context, (ValueComparisonExpression) query);
}
return ret;
}
/**
* If field1 and field2 are both non-arrays:
*
* result=field1 op field2
*
*
* If field1 is an array and field2 is not:
*
* result=false;
* for(i=0;i
*
* If both field1 and field2 are arrays:
*
* op=cmp:
* result=false;
* if(this.field1.length==this.field2.length) {
* result=true;
* for(int i=0;i
*
* If field1 has n ANYs and field2 has none:
*
* for(var i1=0;i1
*
* If field1 has n ANYs and field2 has m anys:
*
* for(var i1=0;i1
*/
private Block translateFieldComparison(Context ctx,FieldComparisonExpression query) {
Path rField = query.getRfield();
FieldTreeNode rFieldMd = ctx.contextNode.resolve(rField);
Path lField = query.getField();
FieldTreeNode lFieldMd = ctx.contextNode.resolve(lField);
Block comparisonBlock=new Block(ctx.topLevel.newGlobalBoolean(ctx));
Block parentBlock=comparisonBlock;
Name lfieldLocalName=new Name();
// First deal with the nested arrays of lField
parentBlock=processNestedArrays(ctx,lField,parentBlock,lfieldLocalName);
// Then deal with the nested arrays of rField
Name rfieldLocalName=new Name();
parentBlock=processNestedArrays(ctx,rField,parentBlock,rfieldLocalName);
Name lfieldName=ctx.varName(lfieldLocalName);
Name rfieldName=ctx.varName(rfieldLocalName);
if(rFieldMd instanceof ArrayField && lFieldMd instanceof ArrayField) {
String loopVar=ctx.newName("i");
// Both fields are arrays
if(query.getOp()==BinaryComparisonOperator._neq) {
parentBlock.add(new SimpleStatement("%s=true",comparisonBlock.resultVar));
SimpleExpression cmp=new SimpleExpression(((ArrayField)rFieldMd).getElement().getType() instanceof DateType &&
((ArrayField)lFieldMd).getElement().getType() instanceof DateType?
"this.%s[%s].valueOf()!=this.%s[%s].valueOf()":
"this.%s[%s]!=this.%s[%s]",
lfieldName,loopVar,
rfieldName,loopVar);
parentBlock.add(IfStatement.ifDefined(lfieldName,rfieldName,
new IfStatement(new SimpleExpression("this.%s.length==this.%s.length",lfieldName,rfieldName),
new SimpleStatement("%s=false",comparisonBlock.resultVar),
new ForLoop(loopVar,true,lfieldName.toString(),
new Block(new IfStatement(cmp,
new SimpleStatement("%s=true",comparisonBlock.resultVar),
SimpleStatement.S_BREAK))))));
} else {
SimpleExpression cmp=new SimpleExpression(((ArrayField)rFieldMd).getElement().getType() instanceof DateType &&
((ArrayField)lFieldMd).getElement().getType() instanceof DateType?
"!(this.%s[%s].valueOf() %s this.%s[%s].valueOf())":
"!(this.%s[%s] %s this.%s[%s])",
lfieldName,loopVar,
BINARY_COMPARISON_OPERATOR_JS_MAP.get(query.getOp()),
rfieldName,loopVar);
parentBlock.add(IfStatement.ifDefined(lfieldName,rfieldName,
new IfStatement(new SimpleExpression("this.%s.length==this.%s.length",lfieldName,rfieldName),
new SimpleStatement("%s=true",comparisonBlock.resultVar),
new ForLoop(loopVar,true,lfieldName.toString(),
new Block(new IfStatement(cmp,
new SimpleStatement("%s=false",comparisonBlock.resultVar),
SimpleStatement.S_BREAK))))));
}
} else if(rFieldMd instanceof ArrayField || lFieldMd instanceof ArrayField) {
// Only one field is an array. If comparison is true for one element, then it is true
Name arrayFieldName;
Name simpleFieldName;
BinaryComparisonOperator op;
boolean isDate;
if(rFieldMd instanceof ArrayField) {
arrayFieldName=rfieldName;
simpleFieldName=lfieldName;
op=query.getOp().invert();
isDate=((ArrayField)rFieldMd).getElement().getType() instanceof DateType && lFieldMd.getType() instanceof DateType;
} else {
arrayFieldName=lfieldName;
simpleFieldName=rfieldName;
op=query.getOp();
isDate=((ArrayField)lFieldMd).getElement().getType() instanceof DateType && rFieldMd.getType() instanceof DateType;
}
String loopVar=ctx.newName("i");
SimpleExpression cmp=new SimpleExpression(isDate?"this.%s[%s].valueOf() %s this.%s.valueOf()":"this.%s[%s] %s this.%s",
arrayFieldName,loopVar,
BINARY_COMPARISON_OPERATOR_JS_MAP.get(op),
simpleFieldName);
parentBlock.add(new ForLoop(loopVar,true,arrayFieldName.toString(),
new Block(new IfStatement(cmp,
new SimpleStatement("%s=true",comparisonBlock.resultVar),
SimpleStatement.S_BREAK))));
} else {
// Simple comparison
parentBlock.add(IfStatement.ifDefined(lfieldName,rfieldName,
new SimpleStatement("%s=this.%s %s this.%s",comparisonBlock.resultVar,lfieldName,BINARY_COMPARISON_OPERATOR_JS_MAP.get(query.getOp()),rfieldName)));
}
// Add breaks to the end of for loops
// Trace ctx back to ctx, add breaks to for loops
IfStatement breakIfNecessary=new IfStatement(new SimpleExpression(comparisonBlock.resultVar),
SimpleStatement.S_BREAK);
Statement parentStmt=parentBlock;
while(parentStmt!=comparisonBlock) {
if(parentStmt instanceof ForLoop)
((ForLoop)parentStmt).add(breakIfNecessary);
parentStmt=((Statement)parentStmt).parent;
}
return comparisonBlock;
}
private Block processNestedArrays(Context ctx,Path field,Block parent,Name arrayFieldName) {
MutablePath pathSegment=new MutablePath();
for(int i=0;i
* in:
* var r0=false;
* nin:
* var r=true;
* for(var i=0;i
*/
private Block translateNaryFieldRelationalExpression(Context ctx,NaryFieldRelationalExpression query) {
FieldTreeNode fieldMd=ctx.contextNode.resolve(query.getField());
if(!fieldMd.getType().supportsEq()) {
throw Error.get(ERR_INVALID_COMPARISON, query.toString());
}
Block block=new Block(ctx.topLevel.newGlobal(ctx,query.getOp()==NaryRelationalOperator._in?"false":"true"));
Name rname=ctx.varName(new Name(query.getRfield()));
Name lname=ctx.varName(new Name(query.getField()));
ArrForLoop loop=new ArrForLoop(ctx.newName("i"),rname);
block.add(IfStatement.ifDefined(rname,lname,loop));
SimpleExpression cmpExpression=new SimpleExpression(fieldMd.getType() instanceof DateType?
"this.%s[%s].valueOf()==this.%s.valueOf()":
"this.%s[%s]==this.%s",rname,
loop.loopVar,
lname);
loop.add(new IfStatement(cmpExpression,
new SimpleStatement("%s=%s",block.resultVar,query.getOp()==NaryRelationalOperator._in?"true":"false"),
SimpleStatement.S_BREAK));
return block;
}
/**
*
* var r0=[values];
* any:
* var r1=false;
* all: none:
* var r1=true;
* var r3=false;
* for(var i=0;i
*/
private Block translateArrayContainsExpression(Context ctx,ArrayContainsExpression query) {
FieldTreeNode fieldMd=ctx.contextNode.resolve(query.getArray());
String valueArr=declareValueArray(ctx,((ArrayField)fieldMd).getElement(),query.getValues());
Block arrayContainsBlock=new Block(ctx.topLevel.newGlobal(ctx,query.getOp()==ContainsOperator._any?"false":"true"));
// for(var i=0;i
* var arr=[values];
*