use of org.apache.atlas.groovy.AbstractFunctionExpression in project incubator-atlas by apache.
the class ExpandAndsOptimization method apply.
/**
* Expands the given and expression. There is no need to recursively
* expand the children here. This method is called recursively by
* GremlinQueryOptimier on the children.
*
*/
@Override
public GroovyExpression apply(GroovyExpression expr, OptimizationContext context) {
FunctionCallExpression exprAsFunction = (FunctionCallExpression) expr;
GroovyExpression result = exprAsFunction.getCaller();
List<GroovyExpression> nonExtractableArguments = new ArrayList<>();
for (GroovyExpression argument : exprAsFunction.getArguments()) {
if (GremlinQueryOptimizer.isExtractable(argument)) {
//Set the caller of the deepest expression in the call hierarchy
//of the argument to point to the current result.
//For example, if result is "g.V()" and the updatedArgument is "has('x').has('y')",
//updatedArgument would be a tree like this:
//
// has('y')
// /
// / caller
// |/_
// has('x')
// /
// / caller
// |/_
// (null)
//
//We would set the caller of has('x') to be g.V(), so result would become g.V().has('x').has('y').
//
// Note: This operation is currently done by making a copy of the argument tree. That should
// be changed.
result = GremlinQueryOptimizer.copyWithNewLeafNode((AbstractFunctionExpression) argument, result);
} else {
logger_.warn("Found non-extractable argument '{}' in the 'and' expression '{}'", argument.toString(), expr.toString());
nonExtractableArguments.add(argument);
}
}
if (!nonExtractableArguments.isEmpty()) {
//add a final 'and' call with the arguments that could not be extracted
result = factory.generateLogicalExpression(result, "and", nonExtractableArguments);
}
return result;
}
use of org.apache.atlas.groovy.AbstractFunctionExpression in project incubator-atlas by apache.
the class ExpandOrsOptimization method moveTransformationsToResultExpression.
private GroovyExpression moveTransformationsToResultExpression(GroovyExpression expr, OptimizationContext context) {
GroovyExpression traveralExpression = expr;
// Determine the 'split point'. This is the expression that will become
// the deepest function call in the result expression. If a split
// point is found, its caller is changed. The new caller is
// set to the graph traversal expression in the result expression.
// The original caller becomes the new traversal expression that
// will be carried through the rest of the 'or' expansion processing.
//
// Example: g.V().has('x').as('x').select('x')
// Here, select('x') is the split expression
// so :
// 1) the result expression in OptimizationContext becomes [base result expression].select('x')
// 2) we return g.V().has('x').as('x')
SplitPointFinder finder = new SplitPointFinder(factory);
GremlinQueryOptimizer.visitCallHierarchy(traveralExpression, finder);
AbstractFunctionExpression splitPoint = finder.getSplitPoint();
List<LiteralExpression> aliases = new ArrayList<>();
//the aliases.
if (splitPoint != null) {
traveralExpression = splitPoint.getCaller();
AliasFinder aliasFinder = new AliasFinder();
GremlinQueryOptimizer.visitCallHierarchy(traveralExpression, aliasFinder);
aliases.addAll(aliasFinder.getAliases());
if (aliasFinder.isFinalAliasNeeded()) {
//The last alias in the expression does not capture the final vertex in the traverser,
//so we need to create an alias to record that.
traveralExpression = factory.generateAliasExpression(traveralExpression, context.getFinalAliasName());
aliases.add(new LiteralExpression(context.getFinalAliasName()));
}
GroovyExpression resultExpr = getBaseResultExpression(context, aliases);
splitPoint.setCaller(resultExpr);
expr = removeMapFromPathsIfNeeded(expr, aliases);
context.setResultExpression(expr);
}
//Add expression(s) to the end of the traversal expression to add the vertices
//that were found into the intermediate variable ('r')
traveralExpression = addCallToUpdateResultVariable(traveralExpression, aliases, context);
return traveralExpression;
}
use of org.apache.atlas.groovy.AbstractFunctionExpression in project incubator-atlas by apache.
the class FunctionGenerator method createFunctionIfNeeded.
/**
* Creates a function whose body goes from the child of parentExpr
* up to (and including) the functionBodyEndExpr.
* @param parentExpr
*/
private void createFunctionIfNeeded(AbstractFunctionExpression parentExpr) {
GroovyExpression potentialFunctionBody = parentExpr.getCaller();
if (creatingFunctionShortensGremlin(potentialFunctionBody)) {
GroovyExpression functionCall = null;
if (nextFunctionBodyStart instanceof AbstractFunctionExpression) {
//The function body start is a a function call. In this
//case, we generate a function that takes one argument, which
//is a graph traversal. We have an expression tree that
//looks kind of like the following:
//
// parentExpr
// /
// / caller
// |/_
// potentialFunctionBody
// /
// / caller
// |/_
// ...
// /
// / caller
// |/_
// nextFunctionBodyStart
// /
// / caller
// |/_
// oldCaller
//
//
// Note that potentialFunctionBody and nextFunctionBodyStart
// could be the same expression. Let's say that the next
// function name is f1
//
// We reshuffle these expressions to the following:
//
// parentExpr
// /
// / caller
// |/_
// f1(oldCaller)
//
//
// potentialFunctionBody <- body of new function "f1(GraphTraversal x)"
// /
// / caller
// |/_
// ...
// /
// / caller
// |/_
// nextFunctionBodyStart
// /
// / caller
// |/_
// x
//
// As an example, suppose parentExpr is g.V().or(x,y).has(a).has(b).has(c)
// where has(a) is nextFunctionBodyStart.
//
// We generate a function f1 = { GraphTraversal x -> x.has(a).has(b) }
// parentExpr would become : f1(g.V().or(x,y)).has(c)
AbstractFunctionExpression nextFunctionBodyStartFunction = (AbstractFunctionExpression) nextFunctionBodyStart;
String variableName = "x";
IdentifierExpression var = new IdentifierExpression(variableName);
GroovyExpression oldCaller = nextFunctionBodyStartFunction.getCaller();
nextFunctionBodyStartFunction.setCaller(var);
currentFunctionName = context.addFunctionDefinition(new VariableDeclaration(factory.getTraversalExpressionClass(), "x"), potentialFunctionBody);
functionCall = new FunctionCallExpression(potentialFunctionBody.getType(), currentFunctionName, oldCaller);
} else {
//The function body start is a not a function call. In this
//case, we generate a function that takes no arguments.
// As an example, suppose parentExpr is g.V().has(a).has(b).has(c)
// where g is nextFunctionBodyStart.
//
// We generate a function f1 = { g.V().has(a).has(b) }
// parentExpr would become : f1().has(c)
currentFunctionName = context.addFunctionDefinition(null, potentialFunctionBody);
functionCall = new FunctionCallExpression(potentialFunctionBody.getType(), currentFunctionName);
}
//functionBodyEnd is now part of a function definition, don't propagate it
nextFunctionBodyStart = null;
parentExpr.setCaller(functionCall);
}
}
use of org.apache.atlas.groovy.AbstractFunctionExpression in project incubator-atlas by apache.
the class ExpandOrsOptimization method expandOrFunction.
/**
* This method takes an 'or' expression and expands it into multiple expressions.
*
* For example:
*
* g.V().or(has('x'),has('y')
*
* is expanded to:
*
* g.V().has('x')
* g.V().has('y')
*
* There are certain cases where it is not safe to move an expression out
* of the 'or'. For example, in the expression
*
* g.V().or(has('x').out('y'),has('z'))
*
* has('x').out('y') cannot be moved out of the 'or', since it changes the value of the traverser.
*
* At this time, the ExpandOrsOptimizer is not able to handle this scenario, so we don't remove
* that expression. In cases like this, a final expression is created that ors together
* all of the expressions that could not be extracted. In this case that would be:
*
* g.V().has('z')
* g.V().or(has('y').out('z'))
*
* This processing is done recursively.
*
*
* @param expr
* @param context
* @return the expressions that should be unioned together to get the query result
*/
private List<GroovyExpression> expandOrFunction(GroovyExpression expr, OptimizationContext context) {
FunctionCallExpression functionCall = (FunctionCallExpression) expr;
GroovyExpression caller = functionCall.getCaller();
List<GroovyExpression> updatedCallers = null;
if (caller != null) {
updatedCallers = expandOrs(caller, context);
} else {
updatedCallers = Collections.singletonList(null);
}
UpdatedExpressions newArguments = getUpdatedChildren(functionCall.getArguments(), context);
List<GroovyExpression> allUpdatedArguments = new ArrayList<>();
for (List<GroovyExpression> exprs : newArguments.getUpdatedChildren()) {
allUpdatedArguments.addAll(exprs);
}
List<AbstractFunctionExpression> extractableArguments = new ArrayList<>();
List<GroovyExpression> nonExtractableArguments = new ArrayList<>();
for (GroovyExpression argument : allUpdatedArguments) {
if (GremlinQueryOptimizer.isExtractable(argument)) {
extractableArguments.add((AbstractFunctionExpression) argument);
} else {
logger_.warn("Found non-extractable argument '{}; in the 'or' expression '{}'", argument.toString(), expr.toString());
nonExtractableArguments.add(argument);
}
}
List<GroovyExpression> result = new ArrayList<>();
for (GroovyExpression updatedCaller : updatedCallers) {
for (AbstractFunctionExpression arg : extractableArguments) {
GroovyExpression updated = GremlinQueryOptimizer.copyWithNewLeafNode(arg, updatedCaller);
result.add(updated);
}
if (!nonExtractableArguments.isEmpty()) {
result.add(factory.generateLogicalExpression(updatedCaller, "or", nonExtractableArguments));
}
}
return result;
}
use of org.apache.atlas.groovy.AbstractFunctionExpression in project incubator-atlas by apache.
the class AbstractGremlinQueryOptimizerTest method testRangeWithNonZeroOffset.
@Test
public void testRangeWithNonZeroOffset() throws Exception {
// g.V().or(has('__typeName','OMAS_OMRSAsset'),has('__superTypeNames','OMAS_OMRSAsset')).range(5,10).as('inst').select('inst')
GroovyExpression toOptimize = getVerticesExpression();
GroovyExpression expr0 = makeHasExpression("__typeName", "OMAS_OMRSAsset");
GroovyExpression expr1 = makeHasExpression("__superTypeNames", "OMAS_OMRSAsset");
toOptimize = getFactory().generateLogicalExpression(toOptimize, "or", Arrays.asList(expr0, expr1));
toOptimize = getFactory().generateRangeExpression(toOptimize, 5, 10);
toOptimize = getFactory().generateAliasExpression(toOptimize, "inst");
toOptimize = getFactory().generateSelectExpression(toOptimize, Collections.singletonList(new LiteralExpression("inst")), Collections.<GroovyExpression>emptyList());
RangeFinder visitor = new RangeFinder(getFactory());
GremlinQueryOptimizer.visitCallHierarchy(toOptimize, visitor);
List<AbstractFunctionExpression> rangeExpressions = visitor.getRangeExpressions();
assertEquals(rangeExpressions.size(), 1);
int[] rangeParameters = getFactory().getRangeParameters(rangeExpressions.get(0));
assertNotNull(rangeParameters);
GroovyExpression optimized = GremlinQueryOptimizer.getInstance().optimize(toOptimize);
// The range optimization is not supported with a non-zero start index, so the optimizer should not add range expressions
// to the expanded or's.
assertEquals(optimized.toString(), getExpectedGremlinForTestRangeWithNonZeroOffset());
}
Aggregations