Search in sources :

Example 1 with AbstractFunctionExpression

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;
}
Also used : ArrayList(java.util.ArrayList) GroovyExpression(org.apache.atlas.groovy.GroovyExpression) AbstractFunctionExpression(org.apache.atlas.groovy.AbstractFunctionExpression) FunctionCallExpression(org.apache.atlas.groovy.FunctionCallExpression)

Example 2 with AbstractFunctionExpression

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;
}
Also used : LiteralExpression(org.apache.atlas.groovy.LiteralExpression) ArrayList(java.util.ArrayList) GroovyExpression(org.apache.atlas.groovy.GroovyExpression) AbstractFunctionExpression(org.apache.atlas.groovy.AbstractFunctionExpression)

Example 3 with AbstractFunctionExpression

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);
    }
}
Also used : GroovyExpression(org.apache.atlas.groovy.GroovyExpression) AbstractFunctionExpression(org.apache.atlas.groovy.AbstractFunctionExpression) VariableDeclaration(org.apache.atlas.groovy.ClosureExpression.VariableDeclaration) FunctionCallExpression(org.apache.atlas.groovy.FunctionCallExpression) IdentifierExpression(org.apache.atlas.groovy.IdentifierExpression)

Example 4 with AbstractFunctionExpression

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;
}
Also used : ArrayList(java.util.ArrayList) GroovyExpression(org.apache.atlas.groovy.GroovyExpression) AbstractFunctionExpression(org.apache.atlas.groovy.AbstractFunctionExpression) FunctionCallExpression(org.apache.atlas.groovy.FunctionCallExpression)

Example 5 with AbstractFunctionExpression

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());
}
Also used : LiteralExpression(org.apache.atlas.groovy.LiteralExpression) GroovyExpression(org.apache.atlas.groovy.GroovyExpression) RangeFinder(org.apache.atlas.gremlin.optimizer.RangeFinder) AbstractFunctionExpression(org.apache.atlas.groovy.AbstractFunctionExpression) Test(org.testng.annotations.Test)

Aggregations

AbstractFunctionExpression (org.apache.atlas.groovy.AbstractFunctionExpression)12 GroovyExpression (org.apache.atlas.groovy.GroovyExpression)11 FunctionCallExpression (org.apache.atlas.groovy.FunctionCallExpression)4 ArrayList (java.util.ArrayList)3 RangeFinder (org.apache.atlas.gremlin.optimizer.RangeFinder)2 LiteralExpression (org.apache.atlas.groovy.LiteralExpression)2 Test (org.testng.annotations.Test)2 VariableDeclaration (org.apache.atlas.groovy.ClosureExpression.VariableDeclaration)1 IdentifierExpression (org.apache.atlas.groovy.IdentifierExpression)1