http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/ExpandOrsOptimization.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/ExpandOrsOptimization.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/ExpandOrsOptimization.java deleted file mode 100644 index a48a007..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/ExpandOrsOptimization.java +++ /dev/null @@ -1,588 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import org.apache.atlas.gremlin.GremlinExpressionFactory; -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.ClosureExpression; -import org.apache.atlas.groovy.FunctionCallExpression; -import org.apache.atlas.groovy.GroovyExpression; -import org.apache.atlas.groovy.LiteralExpression; -import org.apache.atlas.groovy.StatementListExpression; -import org.apache.atlas.groovy.TraversalStepType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.Lists; - - - -/** - * Optimization that removes 'or' expressions from a graph traversal when possible - * and replaces them with separate calls that are combined using a logical union operation. - * Unfortunately, Titan does not use indices when executing the child graph traversals associated - * with an 'or' call. In order to make the index be used, we split queries with - * or expressions into multiple queries. These queries are executed individually, - * using indices, and then the results are combined back together. Here is a - * simple example to illustrate this: - * - * <h4>Original Query</h4> - * - * <pre> - * g.V().or(has('name','Fred'),has('age','17')) - * </pre> - * - *<h4>Optimized Query</h4> - * - * <pre> - * def r = [] as Set; - * g.V().has('name','Fred').fill(r); - * g.V().has('age','17').fill(r); - * r; - * </pre> - * - * Here, we introduce an intermediate variable "r" which is declared as a Set. The Set is performing - * the union for us. If there are vertices that happen to both have "Fred" as the name and "17" as the age, - * the Set will prevent the second query execution from adding a duplicate vertex to the result. Recall that - * in Groovy scripts, the last expression is the one that will be returned back to the caller. We refer to - * that expression is the "result expression". For this example, the result expression is simply "r", which - * contains the vertices that matched the query. - * <p/> - * If the query does any kind of transformation of the vertices to produce the query result, that needs - * to be done in the result expression. To understand why that is, let's take a look at another example: - * - * <h4>Original Query</h4> - * - * <pre> - * g.V().or(has('name','Fred'),has('age','17')).as('person').select('person').by('gender') - * </pre> - * - * <h4>Incorrect Optimized Query</h4> - * - * <pre> - * def r = [] as Set; - * g.V().has('name','Fred').as('person').select('person').by('gender').fill(r) - * g.V().has('age','17').as('person').select('person').by('gender').fill(r) - * r; - * </pre> - * - * The problem with this query is that now 'r' contains Strings (the gender of the person). Suppose - * that there is one person named Fred and there are 3 people whose age is 17 (let's say Fred's age is 16). - * The original query would have produced 4 rows, one corresponding to each of those people. The new - * query would produce at most 2 rows - one for 'male' and one for 'female'. This is happening because - * we are now performing the union on the Strings, not on the vertices. To fix this, we need to split - * the original query and put the end portion into the result expression: - * - * <h4>Correct Optimized Query</h4> - * - * <pre> - * def r = [] as Set; - * g.V().has('name','Fred').fill(r) - * g.V().has('age','17').fill(r) - * __.inject(r as Object[]).as('person').select('person').by('gender') - * </pre> - * - * The logic for doing this splitting is described in more detail in - * {@link #moveTransformationsToResultExpression(GroovyExpression, OptimizationContext)}. - * <p/> - * There is one more problematic case that this optimizer is able to handle. Let's look at the following example: - * - * <h4>Original Query</h4> - * - * <pre> - * g.V().or(has('type','Person'),has('superType','Person')).as('x').has('qualifiedName','Fred').as('y').select('x','y').by('name').by('name') - * </pre> - * - * Queries of this form appear often when translating DSL queries. - * - * If we were to optimize this query using the logic described above, we would get something like this: - * - * <h4>Incorrect Optimized Query</h4> - * - * <pre> - * def r = [] as Set; - * g.V().has('type','Person').fill(r); - * g.V().has('superType','Person').fill(r); - * __.inject(r as Object[]).as('x').has('qualifiedName','Fred').as('y').select('x','y'); - * </pre> - * - * While not strictly incorrect, this query will not perform well since the index on qualifiedName will - * not be used. In order for that index to be used, the 'has' expression needs to be part of the original - * query. However, if we do that alone, the query will be broken, since the select - * will now refer to an undefined label: - * - * <h4>Incorrect Optimized Query</h4> - * - * <pre> - * def r = [] as Set; - * g.V().has('type','Person').as('x').has('qualifiedName','Fred').fill(r); - * g.V().has('superType','Person').as('x').has('qualifiedName','Fred').fill(r); - * __.inject(r as Object[]).as('y').select('x','y') - * </pre> - * - * To fix this, we need to save the values of the aliased vertices in the original - * query, and create labels in the result expression that refer to them. We do this - * as follows: - * - * <h4>Correct Optimized Query</h4> - * - * <pre> - * def r = [] as Set; - * g.V().has('type','Person').as('x').has('qualifiedName','Fred').as('y').select('x','y').fill(r); - * g.V().has('superType','Person').as('x').has('qualifiedName','Fred').select('x','y').fill(r); - * __.inject(r as Object[]).as('__tmp').map({((Map)it.get()).get('x')}).as('x').select('__tmp').map({((Map)it.get()).get('x')}).as('y').select('x','y').by('name').by('name') - * </pre> - * - * This is not pretty, but is the best solution we've found so far for supporting expressions that contain aliases in this optimization. - * What ends up happening is that r gets populated with alias->Vertex maps. In the result expression, we make 'x' point - * to a step where the value in the traverser is the vertex for 'x', and we do the same thing for y. The <code>select('_tmp')</code> step in the middle restores the value of - * the traverser back to the map. - * <p/> - * The one known issue with the alias rearrangement is that it breaks loop expressions. As a result, expressions containing loops are currently excluded - * from this optimization. - * - * ExpandOrsOptimization expands the entire expression tree recursively, so it is not invoked - * recursively by GremlinQueryOptimizer. - * - */ -public class ExpandOrsOptimization implements GremlinOptimization { - - private static final Logger logger_ = LoggerFactory.getLogger(ExpandOrsOptimization.class); - - private final GremlinExpressionFactory factory; - - public ExpandOrsOptimization(GremlinExpressionFactory factory) { - this.factory = factory; - } - - @Override - public boolean appliesTo(GroovyExpression expr, OptimizationContext contxt) { - - ExpressionFinder finder = new ExpressionFinder(IsOr.INSTANCE); - GremlinQueryOptimizer.visitCallHierarchy(expr, finder); - return finder.isExpressionFound(); - } - - @Override - public GroovyExpression apply(GroovyExpression expr, OptimizationContext context) { - - setupRangeOptimization(expr, context); - GroovyExpression traveralExpression = moveTransformationsToResultExpression(expr, context); - - FunctionGenerator functionGenerator = new FunctionGenerator(factory, context); - GremlinQueryOptimizer.visitCallHierarchy(traveralExpression, functionGenerator); - traveralExpression = functionGenerator.getNewRootExpression(); - List<GroovyExpression> bodyExpressions = expandOrs(traveralExpression, context); - - - //Adds a statement to define the result variable 'v' in the - //groovy script. The variable is declared as a Set. The type - //of the objects in the Set depend on the number of aliases in the Groovy - // expression: - // - 0 or 1 alias : Vertex - // - multiple aliases: Map<String,Vertex> - StatementListExpression result = new StatementListExpression(); - context.prependStatement(context.getDefineResultVariableStmt()); - - - for (GroovyExpression bodyExpression : bodyExpressions) { - result.addStatement(bodyExpression); - } - result.addStatement(context.getResultExpression()); - return result; - } - - private void setupRangeOptimization(GroovyExpression expr, OptimizationContext context) { - - // Find any range expressions in the expression tree. - RangeFinder rangeFinder = new RangeFinder(factory); - GremlinQueryOptimizer.visitCallHierarchy(expr, rangeFinder); - List<AbstractFunctionExpression> rangeExpressions = rangeFinder.getRangeExpressions(); - if (rangeExpressions.size() == 1) { - OrderFinder orderFinder = new OrderFinder(factory); - GremlinQueryOptimizer.visitCallHierarchy(expr, orderFinder); - if (!orderFinder.hasOrderExpression()) { - // If there is one range expression and no order expression in the unoptimized gremlin, - // save the range parameters to use for adding a range expression to - // each expanded "or" expression result, such that it will only contain the specified range of vertices. - // For now, apply this optimization only if the range start index is zero. - AbstractFunctionExpression rangeExpression = rangeExpressions.get(0); - int[] rangeParameters = factory.getRangeParameters(rangeExpression); - if (rangeParameters[0] == 0) { - context.setRangeExpression(rangeExpression); - } - } - } - } - - 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<>(); - - //If we're not splitting the query, there is no need to save/restore - //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; - } - - private GroovyExpression removeMapFromPathsIfNeeded(GroovyExpression expr, List<LiteralExpression> aliases) { - if(aliases.size() > 0 && factory.isSelectGeneratesMap(aliases.size())) { - RepeatExpressionFinder repeatExprFinder = new RepeatExpressionFinder(factory); - GremlinQueryOptimizer.visitCallHierarchy(expr, repeatExprFinder); - boolean hasRepeat = repeatExprFinder.isRepeatExpressionFound(); - - PathExpressionFinder pathExprFinder = new PathExpressionFinder(); - GremlinQueryOptimizer.visitCallHierarchy(expr, pathExprFinder); - boolean hasPath = pathExprFinder.isPathExpressionFound(); - if(! hasRepeat && hasPath) { - //the path will now start with the map that we added. That is an artifact - //of the optimization process and must be removed. - if(expr.getType() != TraversalStepType.END && expr.getType() != TraversalStepType.NONE) { - //we're still in the pipeline, need to execute the query before we can - //modify the result - expr = factory.generateToListExpression(expr); - } - expr = factory.removeExtraMapFromPathInResult(expr); - } - - } - return expr; - } - - /** - * This method adds steps to the end of the initial traversal to add the vertices - * that were found into an intermediate variable (defined as a Set). If there is one alias, - * this set will contain the vertices associated with that Alias. If there are multiple - * aliases, the values in the set will be alias->vertex maps that have the vertex - * associated with the alias for each result. - - * @param expr - * @param aliasNames - * @param context - * @return - */ - private GroovyExpression addCallToUpdateResultVariable(GroovyExpression expr,List<LiteralExpression> aliasNames, OptimizationContext context) { - - GroovyExpression result = expr; - // If there is one range expression in the unoptimized gremlin, - // add a range expression here so that the intermediate variable will only contain - // the specified range of vertices. - AbstractFunctionExpression rangeExpression = context.getRangeExpression(); - if (rangeExpression != null) { - int[] rangeParameters = factory.getRangeParameters(rangeExpression); - result = factory.generateRangeExpression(result, rangeParameters[0], rangeParameters[1]); - } - if( ! aliasNames.isEmpty()) { - result = factory.generateSelectExpression(result, aliasNames, Collections.<GroovyExpression>emptyList()); - } - return factory.generateFillExpression(result, context.getResultVariable()); - } - - /** - * Recursively traverses the given expression, expanding or expressions - * wherever they are found. - * - * @param expr - * @param context - * @return expressions that should be unioned together to get the query result - */ - private List<GroovyExpression> expandOrs(GroovyExpression expr, OptimizationContext context) { - - if (GremlinQueryOptimizer.isOrExpression(expr)) { - return expandOrFunction(expr, context); - } - return processOtherExpression(expr, context); - } - - /** - * 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; - } - - private UpdatedExpressions getUpdatedChildren(List<GroovyExpression> children, OptimizationContext context) { - List<List<GroovyExpression>> updatedChildren = new ArrayList<>(); - boolean changed = false; - for (GroovyExpression child : children) { - List<GroovyExpression> childChoices = expandOrs(child, context); - if (childChoices.size() != 1 || childChoices.iterator().next() != child) { - changed = true; - } - updatedChildren.add(childChoices); - } - return new UpdatedExpressions(changed, updatedChildren); - } - - private UpdatedExpressions getUpdatedChildren(GroovyExpression expr, OptimizationContext context) { - return getUpdatedChildren(expr.getChildren(), context); - } - - /** - * This is called when we encounter an expression that is not an "or", for example an "and" expressio. For these - * expressions, we process the children and create copies with the cartesian product of the updated - * arguments. - * - * Example: - * - * g.V().and(or(has('x),has('y'), or(has('a'),has('b'))) - * - * Here, we have an "and" expression with two children: - * - * 1) or(has('x),has('y') - * 2) or(has('a'),has('b')) - * - * We first process these children. They each yield 2 expressions: - * - * 1 -> [ has('x'), has('y') ] - * 2 -> [ has('a'), has('b') ] - * - * The cartesian product of these gives this: - * - * [ has('x'), has('a') ] - * [ has('x'), has('b') ] - * [ has('y'), has('a') ] - * [ has('y'), has('b') ] - * - * So the overall result is: - * - * g.V().and(has('x'), has('a')) - * g.V().and(has('x'), has('b')) - * g.V().and(has('y'), has('a')) - * g.V().and(has('y'), has('b')) - * - * - * @param source - * @param context - * @return expressions that should be unioned together to get the query result - */ - private List<GroovyExpression> processOtherExpression(GroovyExpression source, OptimizationContext context) { - UpdatedExpressions updatedChildren = getUpdatedChildren(source, context); - if (!updatedChildren.hasChanges()) { - return Collections.singletonList(source); - } - List<GroovyExpression> result = new ArrayList<GroovyExpression>(); - - //The updated children list we get back has the possible values for each child - //in the expression. We compute a cartesian product to get all possible - //combinations of child values. - List<List<GroovyExpression>> updateChildLists = Lists.cartesianProduct(updatedChildren.getUpdatedChildren()); - - for (List<GroovyExpression> updatedChildList : updateChildLists) { - result.add(source.copy(updatedChildList)); - } - return result; - } - - @Override - public boolean isApplyRecursively() { - return false; - } - - /** - * - * This method creates a base result expression that recreates the state of the - * graph traverser at start of the result expression to what it would have been - * if we had been executing one Gremlin query (instead of many and doing a union). - * - * To do this, we start with an anonymous graph traversal that will iterate - * through the values in the intermediate Set that was created. We then need - * to set things up so that the aliases that were in the original gremlin query - * refer to steps with the correct traverser value. - * - * The way we do this depends on the number of aliases. If there are 0 or 1 alias, - * the intermediate variable already contains Vertices, so we just create the alias. - * - * If there are multiple aliases, the intermediate variable contains a String->Vertex - * map. We first create a temporary alias that refers to that map. For each alias, - * we use a MapStep to map the map to the Vertex for that alias. We then add back - * the alias, making it refer to the MapStep. Between the alias restorations, we restore the - * traverser object back to the map. - * - * @param context - * @param aliases - * @return - */ - private GroovyExpression getBaseResultExpression(OptimizationContext context, - List<LiteralExpression> aliases) { - - //Start with an anonymous traversal that gets its objects from the intermediate result variable. - GroovyExpression parent = factory.generateSeededTraversalExpresssion(aliases.size() > 1, context.getResultVariable()); - - if(aliases.isEmpty()) { - return parent; - } - - //The expression we will return. - GroovyExpression result = parent; - - //We use a temporary alias to save/restore the original value of the traverser - //at the start of the query. We do this so we can set the value of the traverser - //back to being the map after we retrieve each alias. If there is only one - //alias, the save/restore is not needed, so there is no need to create this alias. - if(aliases.size() > 1) { - - result = factory.generateAliasExpression(result, context.getTempAliasName()); - } - - Iterator<LiteralExpression> it = aliases.iterator(); - while(it.hasNext()) { - LiteralExpression curAlias = it.next(); - //A map is only generated by Gremlin when there is more than one alias. When there is only one - //alias, the intermediate variable will directly contain the vertices.` - if(factory.isSelectGeneratesMap(aliases.size())) { - //Since there is more than one alias, the current traverser object is an alias->vertex - //map. We use a MapStep to map that map to the Vertex for the current alias. This sets - //the current traverser object to that Vertex. We do this by defining the closure we - //pass to the MapStep call [map].get(aliasName) where [map] is the expression - //that refers to the map. - - GroovyExpression rowMapExpr = factory.getCurrentTraverserObject(factory.getClosureArgumentValue()); - GroovyExpression getExpr = factory.generateGetSelectedValueExpression(curAlias, rowMapExpr); - result = factory.generateMapExpression(result, new ClosureExpression(getExpr)); - } - - //Create alias that points to the previous step. The traverser value at that step - //is the Vertex associated with this alias. - result = factory.generateAliasExpression(result, curAlias.getValue().toString()); - if(it.hasNext()) { - //Restore the current value of the traverser back to the current alias->vertex map - result = factory.generateBackReferenceExpression(result, false, context.getTempAliasName()); - } - } - return result; - } - - - - -} -
http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/ExpressionFinder.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/ExpressionFinder.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/ExpressionFinder.java deleted file mode 100644 index 2721049..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/ExpressionFinder.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import com.google.common.base.Function; - -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.GroovyExpression; - -/** - * Call hierarchy visitor that checks if an expression - * matching the specified criteria is present - * in the call hierarch. - */ -public class ExpressionFinder implements CallHierarchyVisitor { - - private final Function<GroovyExpression, Boolean> predicate; - private boolean expressionFound = false; - - public ExpressionFinder(Function<GroovyExpression, Boolean> predicate) { - this.predicate = predicate; - } - @Override - public boolean preVisitFunctionCaller(AbstractFunctionExpression expr) { - if (predicate.apply(expr)) { - expressionFound = true; - return false; - } - return true; - } - - @Override - public void visitNonFunctionCaller(GroovyExpression expr) { - if (predicate.apply(expr)) { - expressionFound = true; - } - } - - @Override - public void visitNullCaller() { - //nothing to do - } - - @Override - public boolean postVisitFunctionCaller(AbstractFunctionExpression functionCall) { - //nothing to do - return true; - } - - public boolean isExpressionFound() { - return expressionFound; - } - -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/FunctionGenerator.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/FunctionGenerator.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/FunctionGenerator.java deleted file mode 100644 index 1a93d0f..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/FunctionGenerator.java +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import java.util.List; - -import org.apache.atlas.gremlin.GremlinExpressionFactory; -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.ClosureExpression; -import org.apache.atlas.groovy.ClosureExpression.VariableDeclaration; -import org.apache.atlas.groovy.FunctionCallExpression; -import org.apache.atlas.groovy.GroovyExpression; -import org.apache.atlas.groovy.IdentifierExpression; - -/** - * Extracts common expressions from an or-containing expression - * into functions. These expressions would otherwise be duplicated - * as part of expanding the "or". Doing this shortens the overall length - * of the Gremlin script so we can maximize query performance. - * - */ -public class FunctionGenerator implements CallHierarchyVisitor { - - //Function length constants. - //These assume we won't reach more than 9 function definition. Even if we do, this is still - //a reasonable approximation. - private static final int INITIAL_FUNCTION_DEF_LENGTH = "def f1={};".length(); - private final int functionDefLength; - private static final int FUNCTION_CALL_OVERHEAD = "f1()".length(); - - /** - * The expression that should be the first (deepest) expression - * in the body of the next generated function. As we go up the - * expression tree in the post visit, this is updated based on the - * expressions we see. During the post visits, if it is null, - * the body expression is set to the expression we're visiting. - * As we go up the tree, it is nulled out if we create a function - * or encounter an or expression. This guarantees that the - * next function body will not contain any or expressions - * and that it will not have expressions that are already - * part of some other function. - */ - private GroovyExpression nextFunctionBodyStart; - - /** - * The number of times expressions will be duplicated. - */ - private int scaleFactor = 1; - - private final OptimizationContext context; - - /** - * The current depth in the expression tree. - */ - private int depth = 0; - - /** - * The name of the last function that was generated. If set, - * we can safely update this function instead of creating a new one. - */ - private String currentFunctionName; - - /** - * The updated expression we will pass back to the caller. - */ - private GroovyExpression newRootExpression; - - private final GremlinExpressionFactory factory; - - public FunctionGenerator(GremlinExpressionFactory factory, OptimizationContext context) { - this.context = context; - this.factory = factory; - functionDefLength = ("def f1={" + factory.getTraversalExpressionClass() + " x->};").length(); - } - - @Override - public boolean preVisitFunctionCaller(AbstractFunctionExpression expr) { - depth++; - if (IsOr.INSTANCE.apply(expr)) { - FunctionCallExpression functionCall = (FunctionCallExpression) expr; - scaleFactor *= functionCall.getArguments().size(); - } - if (newRootExpression == null) { - newRootExpression = expr; - } - - return true; - } - - @Override - public void visitNonFunctionCaller(GroovyExpression expr) { - if (nextFunctionBodyStart == null) { - nextFunctionBodyStart = expr; - } - - } - - @Override - public void visitNullCaller() { - //nothing to do - } - - @Override - public boolean postVisitFunctionCaller(AbstractFunctionExpression expr) { - boolean isRootExpr = depth == 1; - visitParentExpression(expr); - - //The root expression has no parent. To simplify the logic, we create - //a dummy expression so it does have a parent, then call visitParentExpression again - //to examine the root expression. - if (isRootExpr) { - FunctionCallExpression dummyParent = new FunctionCallExpression(expr, "dummy"); - visitParentExpression(dummyParent); - newRootExpression = dummyParent.getCaller(); - } - - depth--; - return true; - } - - /** - * Checks to see if the *caller* of this expression should become part - * of a function. If so, either a new function is created, or the - * expression becomes part of the last function we created. - * - * @param parentExpr - */ - private void visitParentExpression(AbstractFunctionExpression parentExpr) { - - if (nextFunctionBodyStart == null) { - nextFunctionBodyStart = parentExpr; - } - - if (currentFunctionName != null) { - updateCurrentFunction(parentExpr); - } else { - createFunctionIfNeeded(parentExpr); - } - - if (GremlinQueryOptimizer.isOrExpression(parentExpr)) { - //reset - currentFunctionName = null; - //don't include 'or' in generated functions - nextFunctionBodyStart = null; - } - - } - - /** - * 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); - } - } - - /** - * Adds the caller of parentExpr to the current body of the last - * function that was created. - * - * @param parentExpr - */ - private void updateCurrentFunction(AbstractFunctionExpression parentExpr) { - GroovyExpression expr = parentExpr.getCaller(); - if (expr instanceof AbstractFunctionExpression) { - AbstractFunctionExpression exprAsFunction = (AbstractFunctionExpression) expr; - GroovyExpression exprCaller = exprAsFunction.getCaller(); - parentExpr.setCaller(exprCaller); - updateCurrentFunctionDefintion(exprAsFunction); - } - } - - private void updateCurrentFunctionDefintion(AbstractFunctionExpression exprToAdd) { - ClosureExpression functionBodyClosure = context.getUserDefinedFunctionBody(currentFunctionName); - if (functionBodyClosure == null) { - throw new IllegalStateException("User-defined function " + currentFunctionName + " not found!"); - } - List<GroovyExpression> exprs = functionBodyClosure.getStatements(); - GroovyExpression currentFunctionBody = exprs.get(exprs.size() - 1); - //Update the expression so it is called by the current return - //value of the function. - exprToAdd.setCaller(currentFunctionBody); - functionBodyClosure.replaceStatement(exprs.size() - 1, exprToAdd); - } - - //Determines if extracting this expression into a function will shorten - //the overall length of the Groovy script. - private boolean creatingFunctionShortensGremlin(GroovyExpression headExpr) { - int tailLength = getTailLength(); - int length = headExpr.toString().length() - tailLength; - - int overhead = 0; - if (nextFunctionBodyStart instanceof AbstractFunctionExpression) { - overhead = functionDefLength; - } else { - overhead = INITIAL_FUNCTION_DEF_LENGTH; - } - overhead += FUNCTION_CALL_OVERHEAD * scaleFactor; - //length * scaleFactor = space taken by having the expression be inlined [scaleFactor] times - //overhead + length = space taken by the function definition and its calls - return length * scaleFactor > overhead + length; - } - - private int getTailLength() { - if (nextFunctionBodyStart == null) { - return 0; - } - if (!(nextFunctionBodyStart instanceof AbstractFunctionExpression)) { - return 0; - } - AbstractFunctionExpression bodyEndAsFunction = (AbstractFunctionExpression) nextFunctionBodyStart; - return bodyEndAsFunction.getCaller().toString().length(); - } - - public GroovyExpression getNewRootExpression() { - return newRootExpression; - } -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/GremlinOptimization.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/GremlinOptimization.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/GremlinOptimization.java deleted file mode 100644 index bfa45af..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/GremlinOptimization.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import org.apache.atlas.groovy.GroovyExpression; - -/** - * An optimization that can be applied to a gremlin query. - */ -public interface GremlinOptimization { - - /** - * Whether or not this optimization should be applied to the given expression - * @param expr - * @param contxt - * @return - */ - boolean appliesTo(GroovyExpression expr, OptimizationContext contxt); - /** - * Whether or not GremlinQueryOptimizer should call this optimization recursively - * on the updated children. - */ - boolean isApplyRecursively(); - - /** - * Applies the optimization. - * - * @param expr - * @param context - * @return the optimized expression - */ - GroovyExpression apply(GroovyExpression expr, OptimizationContext context); -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/GremlinQueryOptimizer.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/GremlinQueryOptimizer.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/GremlinQueryOptimizer.java deleted file mode 100644 index a0c08fd..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/GremlinQueryOptimizer.java +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.atlas.gremlin.GremlinExpressionFactory; -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.GroovyExpression; -import org.apache.atlas.groovy.StatementListExpression; -import org.apache.atlas.groovy.TraversalStepType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; - - - -/** - * Optimizer for gremlin queries. This class provides a framework for applying optimizations - * to gremlin queries. Each optimization is implemented as a class that implements {@link GremlinOptimization}. - * - * The GremlinQueryOptimizer is the entry point for applying these optimizations. - * - * - */ -public final class GremlinQueryOptimizer { - - private static final Logger LOGGER = LoggerFactory.getLogger(GremlinQueryOptimizer.class); - - - private final List<GremlinOptimization> optimizations = new ArrayList<>(); - - //Allows expression factory to be substituted in unit tests. - private static volatile GremlinExpressionFactory FACTORY = GremlinExpressionFactory.INSTANCE; - - private static volatile GremlinQueryOptimizer INSTANCE = null; - - private GremlinQueryOptimizer() { - - } - - private void addOptimization(GremlinOptimization opt) { - optimizations.add(opt); - } - - public static GremlinQueryOptimizer getInstance() { - if(INSTANCE == null) { - synchronized(GremlinQueryOptimizer.class) { - if(INSTANCE == null) { - GremlinQueryOptimizer createdInstance = new GremlinQueryOptimizer(); - //The order here is important. If there is an "or" nested within an "and", - //that will not be found if ExpandOrsOptimization runs before ExpandAndsOptimization. - createdInstance.addOptimization(new ExpandAndsOptimization(FACTORY)); - createdInstance.addOptimization(new ExpandOrsOptimization(FACTORY)); - INSTANCE = createdInstance; - } - } - } - return INSTANCE; - } - - /** - * For testing only - */ - @VisibleForTesting - public static void setExpressionFactory(GremlinExpressionFactory factory) { - GremlinQueryOptimizer.FACTORY = factory; - } - - /** - * For testing only - */ - @VisibleForTesting - public static void reset() { - INSTANCE = null; - } - - /** - * Optimizes the provided groovy expression. Note that the optimization - * is a <i>destructive</i> process. The source GroovyExpression will be - * modified as part of the optimization process. This is done to avoid - * expensive copying operations where possible. - * - * @param source what to optimize - * @return the optimized query - */ - public GroovyExpression optimize(GroovyExpression source) { - LOGGER.debug("Optimizing gremlin query: " + source); - OptimizationContext context = new OptimizationContext(); - GroovyExpression updatedExpression = source; - for (GremlinOptimization opt : optimizations) { - updatedExpression = optimize(updatedExpression, opt, context); - LOGGER.debug("After "+ opt.getClass().getSimpleName() + ", query = " + updatedExpression); - } - - StatementListExpression result = new StatementListExpression(); - result.addStatements(context.getInitialStatements()); - result.addStatement(updatedExpression); - LOGGER.debug("Final optimized query: " + result.toString()); - return result; - } - - /** - * Optimizes the expression using the given optimization - * @param source - * @param optimization - * @param context - * @return - */ - private GroovyExpression optimize(GroovyExpression source, GremlinOptimization optimization, - OptimizationContext context) { - GroovyExpression result = source; - if (optimization.appliesTo(source, context)) { - //Apply the optimization to the expression. - result = optimization.apply(source, context); - } - if (optimization.isApplyRecursively()) { - //Visit the children, update result with the optimized - //children. - List<GroovyExpression> updatedChildren = new ArrayList<>(); - boolean changed = false; - for (GroovyExpression child : result.getChildren()) { - //Recursively optimize this child. - GroovyExpression updatedChild = optimize(child, optimization, context); - changed |= updatedChild != child; - updatedChildren.add(updatedChild); - } - if (changed) { - //TBD - Can we update in place rather than making a copy? - result = result.copy(updatedChildren); - } - } - return result; - } - - /** - * Visits all expressions in the call hierarchy of an expression. For example, - * in the expression g.V().has('x','y'), the order would be - * <ol> - * <li>pre-visit has('x','y')</li> - * <li>pre-visit V()</li> - * <li>visit g (non-function caller)</li> - * <li>post-visit V()</li> - * <li>post-visit has('x','y')</li> - * </ol> - * @param expr - * @param visitor - */ - public static void visitCallHierarchy(GroovyExpression expr, CallHierarchyVisitor visitor) { - - if (expr == null) { - visitor.visitNullCaller(); - return; - } - if (expr instanceof AbstractFunctionExpression) { - AbstractFunctionExpression functionCall = (AbstractFunctionExpression)expr; - if (!visitor.preVisitFunctionCaller(functionCall)) { - return; - } - GroovyExpression caller = functionCall.getCaller(); - visitCallHierarchy(caller, visitor); - if (!visitor.postVisitFunctionCaller(functionCall)) { - return; - } - } else { - visitor.visitNonFunctionCaller(expr); - } - } - - /** - * Determines if the given expression is an "or" expression. - * @param expr - * @return - */ - public static boolean isOrExpression(GroovyExpression expr) { - return IsOr.INSTANCE.apply(expr); - } - - /** - * Determines whether the given expression can safely - * be pulled out of an and/or expression. - * - * @param expr an argument to an and or or function - * @return - */ - public static boolean isExtractable(GroovyExpression expr) { - - HasForbiddenType hasForbiddenTypePredicate = new HasForbiddenType(FACTORY); - - //alias could conflict with alias in parent traversal - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.SIDE_EFFECT); - - //inlining out(), in() steps will change the result of calls after the and/or() - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.FLAT_MAP_TO_ELEMENTS); - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.FLAT_MAP_TO_VALUES); - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.BARRIER); - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.MAP_TO_ELEMENT); - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.MAP_TO_VALUE); - - //caller expects to be able to continue the traversal. We can't end it - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.END); - - - //we can't inline child traversals - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.SOURCE); - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.START); - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.SIDE_EFFECT); - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.NONE); - hasForbiddenTypePredicate.addForbiddenType(TraversalStepType.BRANCH); - - ExpressionFinder forbiddenExpressionFinder = new ExpressionFinder(hasForbiddenTypePredicate); - GremlinQueryOptimizer.visitCallHierarchy(expr, forbiddenExpressionFinder); - return ! forbiddenExpressionFinder.isExpressionFound(); - } - - /** - * Recursively copies and follows the caller hierarchy of the expression until we come - * to a function call with a null caller. The caller of that expression is set - * to newLeaf. - * - * @param expr - * @param newLeaf - * @return the updated (/copied) expression - */ - public static GroovyExpression copyWithNewLeafNode(AbstractFunctionExpression expr, GroovyExpression newLeaf) { - - - AbstractFunctionExpression result = (AbstractFunctionExpression)expr.copy(); - - //remove leading anonymous traversal expression, if there is one - if(FACTORY.isLeafAnonymousTraversalExpression(expr)) { - result = (AbstractFunctionExpression)newLeaf; - } else { - GroovyExpression newCaller = null; - if (expr.getCaller() == null) { - newCaller = newLeaf; - } else { - newCaller = copyWithNewLeafNode((AbstractFunctionExpression)result.getCaller(), newLeaf); - } - result.setCaller(newCaller); - } - return result; - } - -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/HasForbiddenType.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/HasForbiddenType.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/HasForbiddenType.java deleted file mode 100644 index 3fb9faa..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/HasForbiddenType.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import java.util.HashSet; -import java.util.Set; -import com.google.common.base.Function; - -import org.apache.atlas.gremlin.GremlinExpressionFactory; -import org.apache.atlas.groovy.GroovyExpression; -import org.apache.atlas.groovy.TraversalStepType; - -/** - * Function that tests whether the expression is an 'or' - * graph traversal function. - */ -public final class HasForbiddenType implements Function<GroovyExpression, Boolean> { - - private Set<TraversalStepType> forbiddenTypes = new HashSet<>(); - private final GremlinExpressionFactory factory; - - public HasForbiddenType(GremlinExpressionFactory factory) { - this.factory = factory; - } - - public void addForbiddenType(TraversalStepType type) { - forbiddenTypes.add(type); - } - - @Override - public Boolean apply(GroovyExpression expr) { - if(factory.isLeafAnonymousTraversalExpression(expr)) { - return false; - } - return forbiddenTypes.contains(expr.getType()); - } -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/IsOr.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/IsOr.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/IsOr.java deleted file mode 100644 index ab74087..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/IsOr.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import com.google.common.base.Function; - -import org.apache.atlas.groovy.FunctionCallExpression; -import org.apache.atlas.groovy.GroovyExpression; -import org.apache.atlas.groovy.TraversalStepType; - -/** - * Function that tests whether the expression is an 'or' - * graph traversal function. - */ -public final class IsOr implements Function<GroovyExpression, Boolean> { - - public static final IsOr INSTANCE = new IsOr(); - - private IsOr() { - } - - @Override - public Boolean apply(GroovyExpression expr) { - if (!(expr instanceof FunctionCallExpression)) { - return false; - } - if (expr.getType() != TraversalStepType.FILTER) { - return false; - } - FunctionCallExpression functionCall = (FunctionCallExpression)expr; - return functionCall.getFunctionName().equals("or"); - } -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/IsOrParent.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/IsOrParent.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/IsOrParent.java deleted file mode 100644 index 72085d0..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/IsOrParent.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import com.google.common.base.Function; - -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.FunctionCallExpression; -import org.apache.atlas.groovy.GroovyExpression; -import org.apache.atlas.groovy.TraversalStepType; - -/** - * Matches an expression that gets called after calling or(). For example, - * in g.V().or(x,y).toList(), "toList()" is the "or parent", so calling - * "apply()" on this expression would return true and calling it on all - * the other ones would return false. - */ -public final class IsOrParent implements Function<GroovyExpression, Boolean> { - - public static final IsOrParent INSTANCE = new IsOrParent(); - - private IsOrParent() { - - } - - @Override - public Boolean apply(GroovyExpression expr) { - if (!(expr instanceof AbstractFunctionExpression)) { - return false; - } - AbstractFunctionExpression functionCall = (AbstractFunctionExpression)expr; - GroovyExpression target = functionCall.getCaller(); - - if (!(target instanceof FunctionCallExpression)) { - return false; - } - - if (target.getType() != TraversalStepType.FILTER) { - return false; - } - - FunctionCallExpression targetFunction = (FunctionCallExpression)target; - return targetFunction.getFunctionName().equals("or"); - } -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/OptimizationContext.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/OptimizationContext.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/OptimizationContext.java deleted file mode 100644 index 86c8b98..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/OptimizationContext.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.ClosureExpression; -import org.apache.atlas.groovy.ClosureExpression.VariableDeclaration; -import org.apache.atlas.groovy.GroovyExpression; -import org.apache.atlas.groovy.IdentifierExpression; -import org.apache.atlas.groovy.ListExpression; -import org.apache.atlas.groovy.TypeCoersionExpression; -import org.apache.atlas.groovy.VariableAssignmentExpression; - -/** - * Maintains state information during gremlin optimization. - */ -public class OptimizationContext { - - private static final String TMP_ALIAS_NAME = "__tmp"; - private static final String FINAL_ALIAS_NAME = "__res"; - private static final String RESULT_VARIABLE = "r"; - private final List<GroovyExpression> initialStatements = new ArrayList<>(); - private GroovyExpression resultExpression = getResultVariable(); - private int counter = 1; - private final Map<String, ClosureExpression> functionBodies = new HashMap<>(); - private AbstractFunctionExpression rangeExpression; - - public OptimizationContext() { - - } - - /** - * @return - */ - public List<GroovyExpression> getInitialStatements() { - return initialStatements; - } - - public void prependStatement(GroovyExpression expr) { - initialStatements.add(0, expr); - } - - public String getUniqueFunctionName() { - return "f" + (counter++); - } - - - public GroovyExpression getDefineResultVariableStmt() { - GroovyExpression castExpression = new TypeCoersionExpression(new ListExpression(), "Set"); - GroovyExpression resultVarDef = new VariableAssignmentExpression(RESULT_VARIABLE, castExpression); - return resultVarDef; - - } - public void setResultExpression(GroovyExpression expr) { - resultExpression = expr; - } - - public GroovyExpression getResultExpression() { - return resultExpression; - } - - public GroovyExpression getResultVariable() { - return new IdentifierExpression(RESULT_VARIABLE); - } - - public ClosureExpression getUserDefinedFunctionBody(String functionName) { - return functionBodies.get(functionName); - } - - public String addFunctionDefinition(VariableDeclaration decl, GroovyExpression body) { - String functionName = getUniqueFunctionName(); - List<VariableDeclaration> decls = (decl == null) ? Collections.<VariableDeclaration>emptyList() : Collections.singletonList(decl); - ClosureExpression bodyClosure = new ClosureExpression(body, decls); - VariableAssignmentExpression expr = new VariableAssignmentExpression(functionName, bodyClosure); - initialStatements.add(expr); - functionBodies.put(functionName, bodyClosure); - return functionName; - } - - public String getFinalAliasName() { - return FINAL_ALIAS_NAME; - } - - public String getTempAliasName() { - return TMP_ALIAS_NAME; - } - - public void setRangeExpression(AbstractFunctionExpression rangeExpression) { - this.rangeExpression = rangeExpression; - } - - public AbstractFunctionExpression getRangeExpression() { - return rangeExpression; - } -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/OrderFinder.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/OrderFinder.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/OrderFinder.java deleted file mode 100644 index 792fc52..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/OrderFinder.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import org.apache.atlas.gremlin.GremlinExpressionFactory; -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.GroovyExpression; - - -/** - * Finds order expression in the call hierarchy. - * - */ -public class OrderFinder implements CallHierarchyVisitor { - - private boolean hasOrderExpression; - private GremlinExpressionFactory gremlinFactory; - - public OrderFinder(GremlinExpressionFactory gremlinFactory) { - this.gremlinFactory = gremlinFactory; - } - - @Override - public boolean preVisitFunctionCaller(AbstractFunctionExpression expr) { - - return true; - } - - @Override - public void visitNonFunctionCaller(GroovyExpression expr) { - } - - @Override - public void visitNullCaller() { - } - - @Override - public boolean postVisitFunctionCaller(AbstractFunctionExpression functionCall) { - - if (gremlinFactory.isOrderExpression(functionCall)) { - hasOrderExpression = true; - return false; - } - return true; - } - - - public boolean hasOrderExpression() { - - return hasOrderExpression; - } - -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/PathExpressionFinder.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/PathExpressionFinder.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/PathExpressionFinder.java deleted file mode 100644 index 0e9070d..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/PathExpressionFinder.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.FunctionCallExpression; -import org.apache.atlas.groovy.GroovyExpression; - -/** - * Determines whether an expression contains a path() function. - */ -public class PathExpressionFinder implements CallHierarchyVisitor { - - private boolean found = false; - - @Override - public boolean preVisitFunctionCaller(AbstractFunctionExpression expr) { - if(expr instanceof FunctionCallExpression) { - found = ((FunctionCallExpression)expr).getFunctionName().equals("path"); - if(found) { - return false; - } - } - return true; - } - - @Override - public void visitNonFunctionCaller(GroovyExpression expr) { - - } - - @Override - public void visitNullCaller() { - - } - - public boolean isPathExpressionFound() { - return found; - } - - @Override - public boolean postVisitFunctionCaller(AbstractFunctionExpression functionCall) { - - return false; - } -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/RangeFinder.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/RangeFinder.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/RangeFinder.java deleted file mode 100644 index fa8ca85..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/RangeFinder.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.atlas.gremlin.GremlinExpressionFactory; -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.GroovyExpression; - - -/** - * Finds all range expressions in the call hierarchy. - * - */ -public class RangeFinder implements CallHierarchyVisitor { - - private List<AbstractFunctionExpression> rangeExpressions = new ArrayList<>(); - private GremlinExpressionFactory factory; - - public RangeFinder(GremlinExpressionFactory factory) { - this.factory = factory; - } - - @Override - public boolean preVisitFunctionCaller(AbstractFunctionExpression expr) { - - return true; - } - - @Override - public void visitNonFunctionCaller(GroovyExpression expr) { - } - - @Override - public void visitNullCaller() { - } - - @Override - public boolean postVisitFunctionCaller(AbstractFunctionExpression functionCall) { - - if (factory.isRangeExpression(functionCall)) { - rangeExpressions.add(functionCall); - } - return true; - } - - public List<AbstractFunctionExpression> getRangeExpressions() { - return rangeExpressions; - } - -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/RepeatExpressionFinder.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/RepeatExpressionFinder.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/RepeatExpressionFinder.java deleted file mode 100644 index 8344f36..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/RepeatExpressionFinder.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import org.apache.atlas.gremlin.GremlinExpressionFactory; -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.GroovyExpression; - -/** - * Determines whether an expression contains a repeat/loop function. - */ -public class RepeatExpressionFinder implements CallHierarchyVisitor { - - private boolean found = false; - private GremlinExpressionFactory factory; - - public RepeatExpressionFinder(GremlinExpressionFactory factory) { - this.factory = factory; - } - - @Override - public boolean preVisitFunctionCaller(AbstractFunctionExpression expr) { - - found = factory.isRepeatExpression(expr); - if(found) { - return false; - } - return true; - } - - @Override - public void visitNonFunctionCaller(GroovyExpression expr) { - - } - - @Override - public void visitNullCaller() { - - } - - public boolean isRepeatExpressionFound() { - return found; - } - - @Override - public boolean postVisitFunctionCaller(AbstractFunctionExpression functionCall) { - - return false; - } -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/SplitPointFinder.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/SplitPointFinder.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/SplitPointFinder.java deleted file mode 100644 index f0295e7..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/SplitPointFinder.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.apache.atlas.gremlin.GremlinExpressionFactory; -import org.apache.atlas.groovy.AbstractFunctionExpression; -import org.apache.atlas.groovy.FunctionCallExpression; -import org.apache.atlas.groovy.GroovyExpression; -import org.apache.atlas.groovy.TraversalStepType; - - -/** - * This class finds the first place in the expression where the value of the - * traverser is changed from being a vertex to being something else. This is - * important in the "or" optimization logic, since the union operation must be - * done on *vertices* in order to preserve the semantics of the query. In addition, - * expressions that have side effects must be moved as well, so that those - * side effects will be available to the steps that need them. - */ -public class SplitPointFinder implements CallHierarchyVisitor { - - //Any steps that change the traverser value to something that is not a vertex or edge - //must be included here, so that the union created by ExpandOrsOptimization - //is done over vertices/edges. - private static final Set<TraversalStepType> TYPES_REQUIRED_IN_RESULT_EXPRESSION = new HashSet<>( - Arrays.asList( - TraversalStepType.BARRIER, - TraversalStepType.BRANCH, - TraversalStepType.SIDE_EFFECT, - TraversalStepType.MAP_TO_VALUE, - TraversalStepType.FLAT_MAP_TO_VALUES, - TraversalStepType.END, - TraversalStepType.NONE)); - - private final Set<String> requiredAliases = new HashSet<>(); - - //Exceptions to the requirement that all expressions with a type - //in the above list must be in the result expression. If the - //function name is in this list, it is ok for that expression - //to not be in the result expression. This mechanism allows - //aliases to remain outside the result expression. Other - //exceptions may be found in the future. - private static final Map<TraversalStepType, WhiteList> WHITE_LISTS = new HashMap<>(); - static { - WHITE_LISTS.put(TraversalStepType.SIDE_EFFECT, new WhiteList("as")); - } - - private final GremlinExpressionFactory factory; - - public SplitPointFinder(GremlinExpressionFactory factory) { - this.factory = factory; - } - - /** - * Represents a set of function names. - */ - private static final class WhiteList { - private Set<String> allowedFunctionNames = new HashSet<>(); - public WhiteList(String... names) { - for(String name : names) { - allowedFunctionNames.add(name); - } - } - public boolean contains(String name) { - return allowedFunctionNames.contains(name); - } - } - - private AbstractFunctionExpression splitPoint; - - @Override - public boolean preVisitFunctionCaller(AbstractFunctionExpression expr) { - requiredAliases.addAll(factory.getAliasesRequiredByExpression(expr)); - return true; - } - - @Override - public void visitNonFunctionCaller(GroovyExpression expr) { - - } - - @Override - public void visitNullCaller() { - - } - - public AbstractFunctionExpression getSplitPoint() { - return splitPoint; - } - - @Override - public boolean postVisitFunctionCaller(AbstractFunctionExpression functionCall) { - String aliasName = factory.getAliasNameIfRelevant(functionCall); - if (splitPoint == null) { - - boolean required = isRequiredAlias(aliasName) || - isRequiredInResultExpression(functionCall); - if (required) { - splitPoint = functionCall; - } - } - removeSeenAlias(aliasName); - - return true; - } - - private void removeSeenAlias(String aliasName) { - if(aliasName != null) { - requiredAliases.remove(aliasName); - } - } - - private boolean isRequiredAlias(String aliasName) { - if(aliasName != null) { - return requiredAliases.contains(aliasName); - } - return false; - } - - private boolean isRequiredInResultExpression(AbstractFunctionExpression expr) { - - TraversalStepType type = expr.getType(); - if (!TYPES_REQUIRED_IN_RESULT_EXPRESSION.contains(type)) { - return false; - } - - if(expr instanceof FunctionCallExpression) { - FunctionCallExpression functionCall = (FunctionCallExpression)expr; - //check if the white list permits this function call. If there is - //no white list, all expressions with the current step type must go in the - //result expression. - WhiteList whiteList = WHITE_LISTS.get(type); - if(whiteList != null && whiteList.contains(functionCall.getFunctionName())) { - return false; - } - } - return true; - - } -} http://git-wip-us.apache.org/repos/asf/atlas/blob/0877e47c/repository/src/main/java/org/apache/atlas/gremlin/optimizer/UpdatedExpressions.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/UpdatedExpressions.java b/repository/src/main/java/org/apache/atlas/gremlin/optimizer/UpdatedExpressions.java deleted file mode 100644 index 06351ea..0000000 --- a/repository/src/main/java/org/apache/atlas/gremlin/optimizer/UpdatedExpressions.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.atlas.gremlin.optimizer; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.atlas.groovy.GroovyExpression; - -/** - * Represents a list of updated expressions. - */ -public class UpdatedExpressions { - - private List<List<GroovyExpression>> updatedChildren = new ArrayList<>(); - private boolean changed = false; - - public UpdatedExpressions(boolean changed, List<List<GroovyExpression>> updatedChildren) { - this.changed = changed; - this.updatedChildren = updatedChildren; - } - - public List<List<GroovyExpression>> getUpdatedChildren() { - return updatedChildren; - } - - public boolean hasChanges() { - return changed; - } -}