Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 55D29200BAA for ; Thu, 27 Oct 2016 20:53:31 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 54462160AF6; Thu, 27 Oct 2016 18:53:31 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 4F487160AE6 for ; Thu, 27 Oct 2016 20:53:29 +0200 (CEST) Received: (qmail 9500 invoked by uid 500); 27 Oct 2016 18:53:28 -0000 Mailing-List: contact commits-help@atlas.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@atlas.incubator.apache.org Delivered-To: mailing list commits@atlas.incubator.apache.org Received: (qmail 9491 invoked by uid 99); 27 Oct 2016 18:53:28 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 27 Oct 2016 18:53:28 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id AFBD0C82B9 for ; Thu, 27 Oct 2016 18:53:27 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -6.218 X-Spam-Level: X-Spam-Status: No, score=-6.218 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-2.999, WEIRD_QUOTING=0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id qaS7i5kNjcd4 for ; Thu, 27 Oct 2016 18:53:23 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 102585FC18 for ; Thu, 27 Oct 2016 18:53:21 +0000 (UTC) Received: (qmail 9376 invoked by uid 99); 27 Oct 2016 18:53:21 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 27 Oct 2016 18:53:21 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 2E437E0362; Thu, 27 Oct 2016 18:53:21 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: dkantor@apache.org To: commits@atlas.incubator.apache.org Date: Thu, 27 Oct 2016 18:53:21 -0000 Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: [1/2] incubator-atlas git commit: ATLAS-1195 Clean up DSL Translation (jnhagelb via dkantor) archived-at: Thu, 27 Oct 2016 18:53:31 -0000 Repository: incubator-atlas Updated Branches: refs/heads/master aa15cd0ae -> 69af0ae77 http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/69af0ae7/repository/src/main/java/org/apache/atlas/gremlin/GremlinExpressionFactory.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/gremlin/GremlinExpressionFactory.java b/repository/src/main/java/org/apache/atlas/gremlin/GremlinExpressionFactory.java new file mode 100644 index 0000000..9a36e78 --- /dev/null +++ b/repository/src/main/java/org/apache/atlas/gremlin/GremlinExpressionFactory.java @@ -0,0 +1,409 @@ +/** + * 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_METHOD 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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.atlas.AtlasException; +import org.apache.atlas.groovy.ArithmeticExpression; +import org.apache.atlas.groovy.ClosureExpression; +import org.apache.atlas.groovy.FieldExpression; +import org.apache.atlas.groovy.FunctionCallExpression; +import org.apache.atlas.groovy.GroovyExpression; +import org.apache.atlas.groovy.IdentifierExpression; +import org.apache.atlas.groovy.ListExpression; +import org.apache.atlas.groovy.LiteralExpression; +import org.apache.atlas.groovy.TypeCoersionExpression; +import org.apache.atlas.groovy.VariableAssignmentExpression; +import org.apache.atlas.groovy.ArithmeticExpression.ArithmeticOperator; +import org.apache.atlas.query.GraphPersistenceStrategies; +import org.apache.atlas.query.IntSequence; +import org.apache.atlas.query.TypeUtils.FieldInfo; +import org.apache.atlas.repository.graph.AtlasGraphProvider; +import org.apache.atlas.repository.graphdb.AtlasEdgeDirection; +import org.apache.atlas.repository.graphdb.GremlinVersion; +import org.apache.atlas.typesystem.types.IDataType; + +/** + * Factory to generate Groovy expressions representing Gremlin syntax that that + * are independent of the specific version of Gremlin that is being used. + * + */ +public abstract class GremlinExpressionFactory { + + private static final String G_VARIABLE = "g"; + private static final String IT_VARIABLE = "it"; + + private static final String SET_CLASS = "Set"; + + private static final String OBJECT_FIELD = "object"; + + protected static final String V_METHOD = "V"; + protected static final String FILTER_METHOD = "filter"; + private static final String PATH_METHOD = "path"; + private static final String AS_METHOD = "as"; + private static final String FILL_METHOD = "fill"; + protected static final String HAS_METHOD = "has"; + protected static final String TO_LOWER_CASE_METHOD = "toLowerCase"; + protected static final String SELECT_METHOD = "select"; + protected static final String ORDER_METHOD = "order"; + + public static final GremlinExpressionFactory INSTANCE = AtlasGraphProvider.getGraphInstance() + .getSupportedGremlinVersion() == GremlinVersion.THREE ? new Gremlin3ExpressionFactory() + : new Gremlin2ExpressionFactory(); + + /** + * Gets the expression to use as the parent when translating the loop + * expression in a loop + * + * @param inputQry + * the + * @return + */ + public abstract GroovyExpression getLoopExpressionParent(GroovyExpression inputQry); + + /** + * Generates a loop expression. + * + * @param parent + * the parent of the loop expression + * @param emitExpr + * Expression with the value that should be emitted by the loop + * expression. + * @param loopExpr + * the query expression that is being executed repeatedly + * executed in a loop + * @param alias + * The alias of the expression being looped over + * @param times + * the number of times to repeat, or null if a times condition + * should not be used. + * @return + */ + public abstract GroovyExpression generateLoopExpression(GroovyExpression parent, GraphPersistenceStrategies s, IDataType dataType, + GroovyExpression loopExpr, String alias, Integer times); + + + /** + * Generates a logical (and/or) expression with the given operands. + * @param parent + * @param operator + * @param operands + * @return + */ + public abstract GroovyExpression generateLogicalExpression(GroovyExpression parent, String operator, + List operands); + + /** + * Generates a back reference expression that refers to the given alias. + * + * @param parent + * @param inSelect + * @param alias + * @return + */ + public abstract GroovyExpression generateBackReferenceExpression(GroovyExpression parent, boolean inSelect, + String alias); + + /** + * Generates a select expression + * + * @param parent + * @param sourceNames + * the names of the select fields + * @param srcExprs + * the corresponding values to return + * @return + */ + public abstract GroovyExpression generateSelectExpression(GroovyExpression parent, + List sourceNames, List srcExprs); + + /** + * Generates a an expression that gets the value of the given property from the + * vertex presented by the parent. + * + * @param parent + * @param fInfo + * @param propertyName + * @param inSelect + * @return + */ + public abstract GroovyExpression generateFieldExpression(GroovyExpression parent, FieldInfo fInfo, + String propertyName, boolean inSelect); + + /** + * Generates a has expression that checks whether the vertices match a specific condition + * + * @param s + * @param parent the object that we should call apply the "has" condition to. + * @param propertyName the name of the property whose value we are comparing + * @param symbol comparsion operator symbol ('=','<', etc.) + * @param requiredValue the value to compare against + * @param fInfo info about the field whose value we are checking + * @return + * @throws AtlasException + */ + public abstract GroovyExpression generateHasExpression(GraphPersistenceStrategies s, GroovyExpression parent, + String propertyName, String symbol, GroovyExpression requiredValue, FieldInfo fInfo) throws AtlasException; + + /** + * Generates a limit expression + * + * @param parent + * @param offset + * @param totalRows + * @return + */ + public abstract GroovyExpression generateLimitExpression(GroovyExpression parent, int offset, int totalRows); + + /** + * Generates an order by expression + * + * @param parent + * @param translatedOrderBy + * @param isAscending + * @return + */ + public abstract GroovyExpression generateOrderByExpression(GroovyExpression parent, + List translatedOrderBy, boolean isAscending); + + /** + * Returns the Groovy expressions that should be used as the parents when + * translating an order by expression. This is needed because Gremlin 2 and + * 3 handle order by expressions very differently. + * + */ + public abstract List getOrderFieldParents(); + + /** + * Returns the expression that represents an anonymous graph traversal. + * + * @return + */ + public abstract GroovyExpression getAnonymousTraversalExpression(); + + /** + * Returns an expression representing + * + * @return + */ + public abstract GroovyExpression getFieldInSelect(); + + /** + * Generates the expression the serves as the root of the Gremlin query. + * @param s + * @param varExpr variable containing the vertices to traverse + * @return + */ + protected abstract GroovyExpression initialExpression(GraphPersistenceStrategies s, GroovyExpression varExpr); + + + /** + * Generates an expression that tests whether the vertex represented by the 'toTest' + * expression represents an instance of the specified type, checking both the type + * and super type names. + * + * @param s + * @param typeName + * @param itRef + * @return + */ + protected abstract GroovyExpression typeTestExpression(GraphPersistenceStrategies s, String typeName, + GroovyExpression vertexExpr); + + /** + * Generates a sequence of groovy expressions that filter the vertices to only + * those that match the specified type. If GraphPersistenceStrategies.collectTypeInstancesIntoVar() + * is set, the vertices are put into a variable whose name is geneated from the specified IntSequence. + * The last item in the result will be a graph traversal restricted to only the matching vertices. + */ + public List generateTypeTestExpression(GraphPersistenceStrategies s, GroovyExpression parent, + String typeName, IntSequence intSeq) { + if (s.collectTypeInstancesIntoVar()) { + return typeTestExpressionMultiStep(s, typeName, intSeq); + } else { + return typeTestExpressionUsingFilter(s, parent, typeName); + } + } + + private List typeTestExpressionMultiStep(GraphPersistenceStrategies s, String typeName, + IntSequence intSeq) { + + String varName = "_var_" + intSeq.next(); + GroovyExpression varExpr = new IdentifierExpression(varName); + List result = new ArrayList<>(); + + result.add(newSetVar(varName)); + result.add(fillVarWithTypeInstances(s, typeName, varName)); + result.add(fillVarWithSubTypeInstances(s, typeName, varName)); + result.add(initialExpression(s, varExpr)); + + return result; + } + + private GroovyExpression newSetVar(String varName) { + GroovyExpression castExpr = new TypeCoersionExpression(new ListExpression(), SET_CLASS); + return new VariableAssignmentExpression(varName, castExpr); + } + + private GroovyExpression fillVarWithTypeInstances(GraphPersistenceStrategies s, String typeName, String fillVar) { + GroovyExpression graphExpr = getAllVerticesExpr(); + GroovyExpression typeAttributeNameExpr = new LiteralExpression(s.typeAttributeName()); + GroovyExpression typeNameExpr = new LiteralExpression(typeName); + GroovyExpression hasExpr = new FunctionCallExpression(graphExpr, HAS_METHOD, typeAttributeNameExpr, typeNameExpr); + GroovyExpression fillExpr = new FunctionCallExpression(hasExpr, FILL_METHOD, new IdentifierExpression(fillVar)); + return fillExpr; + } + + private GroovyExpression fillVarWithSubTypeInstances(GraphPersistenceStrategies s, String typeName, + String fillVar) { + GroovyExpression graphExpr = getAllVerticesExpr(); + GroovyExpression superTypeAttributeNameExpr = new LiteralExpression(s.superTypeAttributeName()); + GroovyExpression typeNameExpr = new LiteralExpression(typeName); + GroovyExpression hasExpr = new FunctionCallExpression(graphExpr, HAS_METHOD, superTypeAttributeNameExpr, + typeNameExpr); + GroovyExpression fillExpr = new FunctionCallExpression(hasExpr, FILL_METHOD, new IdentifierExpression(fillVar)); + return fillExpr; + } + + + private List typeTestExpressionUsingFilter(GraphPersistenceStrategies s, GroovyExpression parent, + String typeName) { + GroovyExpression itExpr = getItVariable(); + GroovyExpression typeTestExpr = typeTestExpression(s, typeName, itExpr); + GroovyExpression closureExpr = new ClosureExpression(typeTestExpr); + GroovyExpression filterExpr = new FunctionCallExpression(parent, FILTER_METHOD, closureExpr); + return Collections.singletonList(filterExpr); + } + + + /** + * Generates an expression which checks whether the vertices in the query have + * a field with the given name. + * + * @param parent + * @param fieldName + * @return + */ + public GroovyExpression generateUnaryHasExpression(GroovyExpression parent, String fieldName) { + return new FunctionCallExpression(parent, HAS_METHOD, new LiteralExpression(fieldName)); + } + + /** + * Generates a path expression + * + * @param parent + * @return + */ + public GroovyExpression generatePathExpression(GroovyExpression parent) { + return new FunctionCallExpression(parent, PATH_METHOD); + } + + /** + * Generates the emit expression used in loop expressions. + * @param s + * @param dataType + * @return + */ + protected GroovyExpression generateLoopEmitExpression(GraphPersistenceStrategies s, IDataType dataType) { + return typeTestExpression(s, dataType.getName(), getCurrentObjectExpression()); + } + + /** + * Generates an alias expression + * + * @param parent + * @param alias + * @return + */ + public GroovyExpression generateAliasExpression(GroovyExpression parent, String alias) { + return new FunctionCallExpression(parent, AS_METHOD, new LiteralExpression(alias)); + } + + /** + * Generates an expression that gets the vertices adjacent to the vertex in 'parent' + * in the specified direction. + * + * @param parent + * @param dir + * @return + */ + public GroovyExpression generateAdjacentVerticesExpression(GroovyExpression parent, AtlasEdgeDirection dir) { + return new FunctionCallExpression(parent, getGremlinFunctionName(dir)); + } + + private String getGremlinFunctionName(AtlasEdgeDirection dir) { + switch(dir) { + case IN: + return "in"; + case OUT: + return "out"; + case BOTH: + return "both"; + default: + throw new RuntimeException("Unknown Atlas Edge Direction: " + dir); + } + } + + /** + * Generates an expression that gets the vertices adjacent to the vertex in 'parent' + * in the specified direction, following only edges with the given label. + * + * @param parent + * @param dir + * @return + */ + public GroovyExpression generateAdjacentVerticesExpression(GroovyExpression parent, AtlasEdgeDirection dir, + String label) { + return new FunctionCallExpression(parent, getGremlinFunctionName(dir), new LiteralExpression(label)); + } + + /** + * Generates an arithmetic expression, e.g. a + b + * + */ + public GroovyExpression generateArithmeticExpression(GroovyExpression left, String operator, + GroovyExpression right) throws AtlasException { + ArithmeticOperator op = ArithmeticOperator.lookup(operator); + return new ArithmeticExpression(left, op, right); + } + + + + protected GroovyExpression getItVariable() { + return new IdentifierExpression(IT_VARIABLE); + } + + + protected GroovyExpression getAllVerticesExpr() { + GroovyExpression gExpr = getGraph(); + return new FunctionCallExpression(gExpr, V_METHOD); + } + + protected IdentifierExpression getGraph() { + return new IdentifierExpression(G_VARIABLE); + } + + + protected GroovyExpression getCurrentObjectExpression() { + return new FieldExpression(getItVariable(), OBJECT_FIELD); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/69af0ae7/repository/src/main/scala/org/apache/atlas/query/GraphPersistenceStrategies.scala ---------------------------------------------------------------------- diff --git a/repository/src/main/scala/org/apache/atlas/query/GraphPersistenceStrategies.scala b/repository/src/main/scala/org/apache/atlas/query/GraphPersistenceStrategies.scala index 3ccc53f..eb6d119 100755 --- a/repository/src/main/scala/org/apache/atlas/query/GraphPersistenceStrategies.scala +++ b/repository/src/main/scala/org/apache/atlas/query/GraphPersistenceStrategies.scala @@ -21,23 +21,20 @@ package org.apache.atlas.query import java.util import java.util.Date +import scala.collection.JavaConversions._ import scala.collection.JavaConversions.seqAsJavaList +import scala.language.existentials -import org.apache.atlas.query.Expressions.{ComparisonExpression, ExpressionException} +import org.apache.atlas.groovy.GroovyExpression import org.apache.atlas.query.TypeUtils.FieldInfo -import org.apache.atlas.repository.graph.{GraphHelper, GraphBackedMetadataRepository} import org.apache.atlas.repository.RepositoryException +import org.apache.atlas.repository.graph.GraphHelper import org.apache.atlas.repository.graphdb._ -import org.apache.atlas.typesystem.persistence.Id -import org.apache.atlas.typesystem.types.DataTypes._ +import org.apache.atlas.typesystem.ITypedInstance +import org.apache.atlas.typesystem.ITypedReferenceableInstance import org.apache.atlas.typesystem.persistence.Id import org.apache.atlas.typesystem.types._ -import org.apache.atlas.typesystem.{ITypedInstance, ITypedReferenceableInstance} - - -import scala.collection.JavaConversions._ -import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer +import org.apache.atlas.typesystem.types.DataTypes._ /** * Represents the Bridge between the QueryProcessor and the Graph Persistence scheme used. @@ -47,19 +44,19 @@ import scala.collection.mutable.ArrayBuffer * - how are attribute names mapped to Property Keys in Vertices. * * This is a work in progress. - * + * */ trait GraphPersistenceStrategies { @throws(classOf[RepositoryException]) def getGraph() : AtlasGraph[_,_] - + def getSupportedGremlinVersion() : GremlinVersion = getGraph().getSupportedGremlinVersion; - def generatePersisentToLogicalConversionExpression(expr: String, t: IDataType[_]) : String = getGraph().generatePersisentToLogicalConversionExpression(expr, t); + def generatePersisentToLogicalConversionExpression(expr: GroovyExpression, t: IDataType[_]) : GroovyExpression = getGraph().generatePersisentToLogicalConversionExpression(expr, t); def isPropertyValueConversionNeeded(attrType: IDataType[_]) : Boolean = getGraph().isPropertyValueConversionNeeded(attrType); - - def initialQueryCondition = if (getGraph().requiresInitialIndexedPredicate()) { s""".${getGraph().getInitialIndexedPredicate}""" } else {""}; - + + def addInitialQueryCondition(parent: GroovyExpression) : GroovyExpression = if (getGraph().requiresInitialIndexedPredicate()) { getGraph().getInitialIndexedPredicate(parent) } else { parent }; + /** * Name of attribute used to store typeName in vertex */ @@ -87,11 +84,12 @@ trait GraphPersistenceStrategies { def traitLabel(cls: IDataType[_], traitName: String): String - def instanceToTraitEdgeDirection : String = "out" - def traitToInstanceEdgeDirection = instanceToTraitEdgeDirection match { - case "out" => "in" - case "in" => "out" - case x => x + def instanceToTraitEdgeDirection : AtlasEdgeDirection = AtlasEdgeDirection.OUT; + + def traitToInstanceEdgeDirection : AtlasEdgeDirection = instanceToTraitEdgeDirection match { + case AtlasEdgeDirection.OUT => AtlasEdgeDirection.IN; + case AtlasEdgeDirection.IN => AtlasEdgeDirection.OUT; + case x => AtlasEdgeDirection.IN; } /** @@ -115,27 +113,6 @@ trait GraphPersistenceStrategies { case FieldInfo(dataType, null, null, traitName) => traitLabel(dataType, traitName) } - def fieldPrefixInSelect(): String = { - - if(getSupportedGremlinVersion() == GremlinVersion.THREE) { - //this logic is needed to remove extra results from - //what is emitted by repeat loops. Technically - //for queries that don't have a loop in them we could just use "it" - //the reason for this is that in repeat loops with an alias, - //although the alias gets set to the right value, for some - //reason the select actually includes all vertices that were traversed - //through in the loop. In these cases, we only want the last vertex - //traversed in the loop to be selected. The logic here handles that - //case by converting the result to a list and just selecting the - //last item from it. - "((it as Vertex[]) as List).last()" - } - else { - "it" - } - - } - /** * extract the Id from a Vertex. * @param dataTypeNm the dataType of the instance that the given vertex represents @@ -146,50 +123,7 @@ trait GraphPersistenceStrategies { def constructInstance[U](dataType: IDataType[U], v: java.lang.Object): U - def gremlinCompOp(op: ComparisonExpression) = { - if( getSupportedGremlinVersion() == GremlinVersion.TWO) { - gremlin2CompOp(op); - } - else { - gremlin3CompOp(op); - } - } - - def gremlinPrimitiveOp(op: ComparisonExpression) = op.symbol match { - case "=" => "==" - case "!=" => "!=" - case ">" => ">" - case ">=" => ">=" - case "<" => "<" - case "<=" => "<=" - case _ => throw new ExpressionException(op, "Comparison operator not supported in Gremlin") - } - - private def gremlin2CompOp(op: ComparisonExpression) = op.symbol match { - case "=" => "T.eq" - case "!=" => "T.neq" - case ">" => "T.gt" - case ">=" => "T.gte" - case "<" => "T.lt" - case "<=" => "T.lte" - case _ => throw new ExpressionException(op, "Comparison operator not supported in Gremlin") - } - - private def gremlin3CompOp(op: ComparisonExpression) = op.symbol match { - case "=" => "eq" - case "!=" => "neq" - case ">" => "gt" - case ">=" => "gte" - case "<" => "lt" - case "<=" => "lte" - case _ => throw new ExpressionException(op, "Comparison operator not supported in Gremlin") - } - - def loopObjectExpression(dataType: IDataType[_]) = { - _typeTestExpression(dataType.getName, "it.object") - } - - def addGraphVertexPrefix(preStatements : Traversable[String]) = !collectTypeInstancesIntoVar + def addGraphVertexPrefix(preStatements : Traversable[GroovyExpression]) = !collectTypeInstancesIntoVar /** * Controls behavior of how instances of a Type are discovered. @@ -213,76 +147,14 @@ trait GraphPersistenceStrategies { */ def collectTypeInstancesIntoVar = true - def typeTestExpression(typeName : String, intSeq : IntSequence) : Seq[String] = { - if (collectTypeInstancesIntoVar) - typeTestExpressionMultiStep(typeName, intSeq) - else - typeTestExpressionUsingFilter(typeName) - } - - private def typeTestExpressionUsingFilter(typeName : String) : Seq[String] = { - Seq(s"""filter${_typeTestExpression(typeName, "it")}""") - } - - /** - * type test expression that ends up in the emit clause in - * loop/repeat steps and a few other places - */ - private def _typeTestExpression(typeName: String, itRef: String): String = { - - if( getSupportedGremlinVersion() == GremlinVersion.TWO) { - s"""{(${itRef}.'${typeAttributeName}' == '${typeName}') | - | (${itRef}.'${superTypeAttributeName}' ? - | ${itRef}.'${superTypeAttributeName}'.contains('${typeName}') : false)}""". - stripMargin.replace(System.getProperty("line.separator"), "") - } - else { - //gremlin 3 - s"""has('${typeAttributeName}',eq('${typeName}')).or().has('${superTypeAttributeName}',eq('${typeName}'))""" - } - } private def propertyValueSet(vertexRef : String, attrName: String) : String = { s"""org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils.set(${vertexRef}.values('${attrName})""" } - private def typeTestExpressionMultiStep(typeName : String, intSeq : IntSequence) : Seq[String] = { - - val varName = s"_var_${intSeq.next}" - Seq( - newSetVar(varName), - fillVarWithTypeInstances(typeName, varName), - fillVarWithSubTypeInstances(typeName, varName), - if(getSupportedGremlinVersion() == GremlinVersion.TWO) { - s"$varName._()" - } - else { - //this bit of groovy magic converts the set of vertices in varName into - //a String containing the ids of all the vertices. This becomes the argument - //to g.V(). This is needed because Gremlin 3 does not support - // _() - //s"g.V(${varName}.collect{it.id()} as String[])" - s"g.V(${varName} as Object[])${initialQueryCondition}" - } - ) - } - - private def newSetVar(varName : String) = s"def $varName = [] as Set" - - private def fillVarWithTypeInstances(typeName : String, fillVar : String) = { - s"""g.V().has("${typeAttributeName}", "${typeName}").fill($fillVar)""" - } - private def fillVarWithSubTypeInstances(typeName : String, fillVar : String) = { - s"""g.V().has("${superTypeAttributeName}", "${typeName}").fill($fillVar)""" - } } -import scala.language.existentials; -import org.apache.atlas.repository.RepositoryException -import org.apache.atlas.repository.RepositoryException -import org.apache.atlas.repository.RepositoryException -import org.apache.atlas.repository.RepositoryException case class GraphPersistenceStrategy1(g: AtlasGraph[_,_]) extends GraphPersistenceStrategies { @@ -293,8 +165,8 @@ case class GraphPersistenceStrategy1(g: AtlasGraph[_,_]) extends GraphPersistenc override def getGraph() : AtlasGraph[_,_] = { return g; - } - + } + def edgeLabel(dataType: IDataType[_], aInfo: AttributeInfo) = s"__${dataType.getName}.${aInfo.name}" def edgeLabel(propertyName: String) = s"__${propertyName}" @@ -458,8 +330,6 @@ case class GraphPersistenceStrategy1(g: AtlasGraph[_,_]) extends GraphPersistenc } } - - private def mapVertexToCollectionEntry(instanceVertex: AtlasVertex[_,_], attributeInfo: AttributeInfo, elementType: IDataType[_], i: ITypedInstance, value: Any): Any = { elementType.getTypeCategory match { case DataTypes.TypeCategory.PRIMITIVE => value @@ -474,5 +344,6 @@ case class GraphPersistenceStrategy1(g: AtlasGraph[_,_]) extends GraphPersistenc throw new UnsupportedOperationException(s"load for ${attributeInfo.dataType()} not supported") } } + } http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/69af0ae7/repository/src/main/scala/org/apache/atlas/query/GremlinQuery.scala ---------------------------------------------------------------------- diff --git a/repository/src/main/scala/org/apache/atlas/query/GremlinQuery.scala b/repository/src/main/scala/org/apache/atlas/query/GremlinQuery.scala index 447622b..ee58eee 100755 --- a/repository/src/main/scala/org/apache/atlas/query/GremlinQuery.scala +++ b/repository/src/main/scala/org/apache/atlas/query/GremlinQuery.scala @@ -18,30 +18,61 @@ package org.apache.atlas.query +import java.lang.Boolean +import java.lang.Byte +import java.lang.Double +import java.lang.Float +import java.lang.Integer +import java.lang.Long +import java.lang.Short +import java.util.ArrayList + +import scala.collection.JavaConversions.asScalaBuffer +import scala.collection.JavaConversions.bufferAsJavaList import scala.collection.mutable import scala.collection.mutable.ArrayBuffer -import org.apache.atlas.query.TypeUtils.FieldInfo; -import org.apache.atlas.query.Expressions._ -import org.apache.atlas.repository.graphdb.GremlinVersion +import org.apache.atlas.gremlin.GremlinExpressionFactory +import org.apache.atlas.groovy.CastExpression +import org.apache.atlas.groovy.CodeBlockExpression +import org.apache.atlas.groovy.FunctionCallExpression +import org.apache.atlas.groovy.GroovyExpression +import org.apache.atlas.groovy.GroovyGenerationContext +import org.apache.atlas.groovy.IdentifierExpression +import org.apache.atlas.groovy.ListExpression +import org.apache.atlas.groovy.LiteralExpression +import org.apache.atlas.query.Expressions.AliasExpression +import org.apache.atlas.query.Expressions.ArithmeticExpression +import org.apache.atlas.query.Expressions.BackReference +import org.apache.atlas.query.Expressions.ClassExpression +import org.apache.atlas.query.Expressions.ComparisonExpression +import org.apache.atlas.query.Expressions.Expression +import org.apache.atlas.query.Expressions.ExpressionException +import org.apache.atlas.query.Expressions.FieldExpression +import org.apache.atlas.query.Expressions.FilterExpression +import org.apache.atlas.query.Expressions.InstanceExpression +import org.apache.atlas.query.Expressions.LimitExpression +import org.apache.atlas.query.Expressions.ListLiteral +import org.apache.atlas.query.Expressions.Literal +import org.apache.atlas.query.Expressions.LogicalExpression +import org.apache.atlas.query.Expressions.LoopExpression +import org.apache.atlas.query.Expressions.OrderExpression +import org.apache.atlas.query.Expressions.PathExpression +import org.apache.atlas.query.Expressions.SelectExpression +import org.apache.atlas.query.Expressions.TraitExpression +import org.apache.atlas.query.Expressions.TraitInstanceExpression +import org.apache.atlas.query.Expressions.hasFieldLeafExpression +import org.apache.atlas.query.Expressions.hasFieldUnaryExpression +import org.apache.atlas.query.Expressions.id +import org.apache.atlas.query.Expressions.isTraitLeafExpression +import org.apache.atlas.query.Expressions.isTraitUnaryExpression +import org.apache.atlas.repository.RepositoryException +import org.apache.atlas.repository.graphdb.AtlasEdgeDirection import org.apache.atlas.typesystem.types.DataTypes import org.apache.atlas.typesystem.types.DataTypes.TypeCategory import org.apache.atlas.typesystem.types.IDataType -import org.apache.commons.lang.StringEscapeUtils import org.apache.atlas.typesystem.types.TypeSystem -import org.apache.atlas.typesystem.types.AttributeInfo import org.joda.time.format.ISODateTimeFormat -import org.apache.atlas.typesystem.types.DataTypes.BigDecimalType -import org.apache.atlas.typesystem.types.DataTypes.ByteType -import org.apache.atlas.typesystem.types.DataTypes.BooleanType -import org.apache.atlas.typesystem.types.DataTypes.DateType -import org.apache.atlas.typesystem.types.DataTypes.BigIntegerType -import org.apache.atlas.typesystem.types.DataTypes.IntType -import org.apache.atlas.typesystem.types.DataTypes.StringType -import org.apache.atlas.typesystem.types.DataTypes.LongType -import org.apache.atlas.typesystem.types.DataTypes.DoubleType -import org.apache.atlas.typesystem.types.DataTypes.FloatType -import org.apache.atlas.typesystem.types.DataTypes.ShortType trait IntSequence { def next: Int @@ -127,7 +158,7 @@ trait SelectExpressionHandling { /** * For each Output Column in the SelectExpression compute the ArrayList(Src) this maps to and the position within * this list. - * + * * @param sel * @return */ @@ -151,8 +182,8 @@ class GremlinTranslator(expr: Expression, gPersistenceBehavior: GraphPersistenceStrategies) extends SelectExpressionHandling { - val preStatements = ArrayBuffer[String]() - val postStatements = ArrayBuffer[String]() + val preStatements = ArrayBuffer[GroovyExpression]() + val postStatements = ArrayBuffer[GroovyExpression]() val wrapAndRule: PartialFunction[Expression, Expression] = { case f: FilterExpression if !f.condExpr.isInstanceOf[LogicalExpression] => @@ -216,351 +247,287 @@ class GremlinTranslator(expr: Expression, } } - def typeTestExpression(typeName : String) : String = { - val stats = gPersistenceBehavior.typeTestExpression(escape(typeName), counter) + def typeTestExpression(parent: GroovyExpression, typeName : String) : GroovyExpression = { + val stats = GremlinExpressionFactory.INSTANCE.generateTypeTestExpression(gPersistenceBehavior, parent, typeName, counter) + preStatements ++= stats.init stats.last + } - def escape(str: String): String = { - if (str != null) { - return str.replace("\"", "\\\"").replace("$", "\\$"); - } - str + val QUOTE = "\""; + + private def cleanStringLiteral(l : Literal[_]) : String = { + return l.toString.stripPrefix(QUOTE).stripSuffix(QUOTE); } - - private def genQuery(expr: Expression, inSelect: Boolean): String = expr match { - case ClassExpression(clsName) => - typeTestExpression(clsName) - case TraitExpression(clsName) => - typeTestExpression(clsName) + + + private def genQuery(parent: GroovyExpression, expr: Expression, inSelect: Boolean): GroovyExpression = expr match { + case ClassExpression(clsName) => typeTestExpression(parent, clsName) + case TraitExpression(clsName) => typeTestExpression(parent, clsName) case fe@FieldExpression(fieldName, fInfo, child) if fe.dataType.getTypeCategory == TypeCategory.PRIMITIVE || fe.dataType.getTypeCategory == TypeCategory.ARRAY => { - val fN = "\"" + gPersistenceBehavior.fieldNameInVertex(fInfo.dataType, fInfo.attrInfo) + "\"" - genPropertyAccessExpr(child, fInfo, fN, inSelect) - - } + val fN = gPersistenceBehavior.fieldNameInVertex(fInfo.dataType, fInfo.attrInfo) + val childExpr = translateOptChild(parent, child, inSelect); + return GremlinExpressionFactory.INSTANCE.generateFieldExpression(childExpr, fInfo, fN, inSelect); + } case fe@FieldExpression(fieldName, fInfo, child) if fe.dataType.getTypeCategory == TypeCategory.CLASS || fe.dataType.getTypeCategory == TypeCategory.STRUCT => { - val direction = if (fInfo.isReverse) "in" else "out" + val childExpr = translateOptChild(parent, child, inSelect); + val direction = if (fInfo.isReverse) AtlasEdgeDirection.IN else AtlasEdgeDirection.OUT val edgeLbl = gPersistenceBehavior.edgeLabel(fInfo) - val step = s"""$direction("$edgeLbl")""" - generateAndPrependExpr(child, inSelect, s"""$step""") - } + return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(childExpr, direction, edgeLbl) + + } case fe@FieldExpression(fieldName, fInfo, child) if fInfo.traitName != null => { + val childExpr = translateOptChild(parent, child, inSelect); val direction = gPersistenceBehavior.instanceToTraitEdgeDirection val edgeLbl = gPersistenceBehavior.edgeLabel(fInfo) - val step = s"""$direction("$edgeLbl")""" - generateAndPrependExpr(child, inSelect, s"""$step""") + return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(childExpr, direction, edgeLbl) + } case c@ComparisonExpression(symb, f@FieldExpression(fieldName, fInfo, ch), l) => { - return genHasPredicate(ch, fInfo, fieldName, inSelect, c, l) + val qualifiedPropertyName = s"${gPersistenceBehavior.fieldNameInVertex(fInfo.dataType, fInfo.attrInfo)}" + + val childExpr = translateOptChild(parent, ch, inSelect) + val persistentExprValue : GroovyExpression = if(l.isInstanceOf[Literal[_]]) { + translateLiteralValue(fInfo.attrInfo.dataType, l.asInstanceOf[Literal[_]]); + } + else { + genQuery(null, l, inSelect); + } + + return GremlinExpressionFactory.INSTANCE.generateHasExpression(gPersistenceBehavior, childExpr, qualifiedPropertyName, c.symbol, persistentExprValue, fInfo); } case fil@FilterExpression(child, condExpr) => { - s"${genQuery(child, inSelect)}.${genQuery(condExpr, inSelect)}" + val newParent = genQuery(parent, child, inSelect); + return genQuery(newParent, condExpr, inSelect); } case l@LogicalExpression(symb, children) => { - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.THREE) { - if(children.length == 1) { - //gremlin 3 treats one element expressions as 'false'. Avoid - //creating a boolean expression in this case. Inline the expression - //note: we can't simply omit it, since it will cause us to traverse the edge! - //use 'where' instead - var child : Expression = children.head; - //if child is a back expression, that expression becomes an argument to where - - return s"""where(${genQuery(child, inSelect)})"""; - } - else { - // Gremlin 3 does not support _() syntax - // - return s"""$symb${children.map( genQuery(_, inSelect)).mkString("(", ",", ")")}""" - } - } - else { - s"""$symb${children.map("_()." + genQuery(_, inSelect)).mkString("(", ",", ")")}""" - } + val translatedChildren : java.util.List[GroovyExpression] = translateList(children, true, inSelect); + return GremlinExpressionFactory.INSTANCE.generateLogicalExpression(parent, symb, translatedChildren); } case sel@SelectExpression(child, selList) => { val m = groupSelectExpressionsBySrc(sel) - var srcNamesList: List[String] = List() - var srcExprsList: List[List[String]] = List() + var srcNamesList: java.util.List[LiteralExpression] = new ArrayList() + var srcExprsList: List[java.util.List[GroovyExpression]] = List() val it = m.iterator + while (it.hasNext) { val (src, selExprs) = it.next - srcNamesList = srcNamesList :+ s""""$src"""" - srcExprsList = srcExprsList :+ selExprs.map { selExpr => - genQuery(selExpr, true) - } + srcNamesList.add(new LiteralExpression(src)); + val translatedSelExprs : java.util.List[GroovyExpression] = translateList(selExprs, false, true); + srcExprsList = srcExprsList :+ translatedSelExprs } - val srcExprsStringList = srcExprsList.map { - _.mkString("[", ",", "]") + val srcExprsStringList : java.util.List[GroovyExpression] = new ArrayList(); + srcExprsList.foreach { it => + srcExprsStringList.add(new ListExpression(it)); } - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) { - val srcNamesString = srcNamesList.mkString("[", ",", "]") - val srcExprsString = srcExprsStringList.foldLeft("")(_ + "{" + _ + "}") - s"${genQuery(child, inSelect)}.select($srcNamesString)$srcExprsString" - } - else { - //gremlin 3 - val srcNamesString = srcNamesList.mkString("", ",", "") - val srcExprsString = srcExprsStringList.foldLeft("")(_ + ".by({" + _ + "} as Function)") - s"${genQuery(child, inSelect)}.select($srcNamesString)$srcExprsString" - } + val childExpr = genQuery(parent, child, inSelect) + return GremlinExpressionFactory.INSTANCE.generateSelectExpression(childExpr, srcNamesList, srcExprsStringList); + } case loop@LoopExpression(input, loopExpr, t) => { - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) { - val inputQry = genQuery(input, inSelect) - val loopingPathGExpr = genQuery(loopExpr, inSelect) - val loopGExpr = s"""loop("${input.asInstanceOf[AliasExpression].alias}")""" - val untilCriteria = if (t.isDefined) s"{it.loops < ${t.get.value}}" else "{it.path.contains(it.object)?false:true}" - val loopObjectGExpr = gPersistenceBehavior.loopObjectExpression(input.dataType) - val enablePathExpr = s".enablePath()" - s"""${inputQry}.${loopingPathGExpr}.${loopGExpr}${untilCriteria}${loopObjectGExpr}${enablePathExpr}""" + val times : Integer = if(t.isDefined) { + t.get.rawValue.asInstanceOf[Integer] } else { - //gremlin 3 - TODO - add support for circular lineage - val inputQry = genQuery(input, inSelect) - val repeatExpr = s"""repeat(__.${genQuery(loopExpr, inSelect)})""" - val optTimesExpr = if (t.isDefined) s".times(${t.get.value})" else "" - val emitExpr = s""".emit(${gPersistenceBehavior.loopObjectExpression(input.dataType)})""" - - s"""${inputQry}.${repeatExpr}${optTimesExpr}${emitExpr}""" - + null.asInstanceOf[Integer] } + val alias = input.asInstanceOf[AliasExpression].alias; + val inputQry = genQuery(parent, input, inSelect) + val translatedLoopExpr = genQuery(GremlinExpressionFactory.INSTANCE.getLoopExpressionParent(inputQry), loopExpr, inSelect); + return GremlinExpressionFactory.INSTANCE.generateLoopExpression(inputQry, gPersistenceBehavior, input.dataType, translatedLoopExpr, alias, times); } case BackReference(alias, _, _) => { - if (inSelect) { - gPersistenceBehavior.fieldPrefixInSelect() - } - else { - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) { - s"""back("$alias")""" - } - else { - s"""select("$alias")""" - } - } + return GremlinExpressionFactory.INSTANCE.generateBackReferenceExpression(parent, inSelect, alias); + } + case AliasExpression(child, alias) => { + var childExpr = genQuery(parent, child, inSelect); + return GremlinExpressionFactory.INSTANCE.generateAliasExpression(childExpr, alias); + } + case isTraitLeafExpression(traitName, Some(clsExp)) => { + val label = gPersistenceBehavior.traitLabel(clsExp.dataType, traitName); + return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(parent, AtlasEdgeDirection.OUT, label); + } + case isTraitUnaryExpression(traitName, child) => { + val label = gPersistenceBehavior.traitLabel(child.dataType, traitName); + return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(parent, AtlasEdgeDirection.OUT, label); } - case AliasExpression(child, alias) => s"""${genQuery(child, inSelect)}.as("$alias")""" - case isTraitLeafExpression(traitName, Some(clsExp)) => - s"""out("${gPersistenceBehavior.traitLabel(clsExp.dataType, traitName)}")""" - case isTraitUnaryExpression(traitName, child) => - s"""out("${gPersistenceBehavior.traitLabel(child.dataType, traitName)}")""" case hasFieldLeafExpression(fieldName, clsExp) => clsExp match { - case None => s"""has("$fieldName")""" + case None => GremlinExpressionFactory.INSTANCE.generateUnaryHasExpression(parent, fieldName) case Some(x) => { val fi = TypeUtils.resolveReference(clsExp.get.dataType, fieldName); if(! fi.isDefined) { - s"""has("$fieldName")""" + return GremlinExpressionFactory.INSTANCE.generateUnaryHasExpression(parent, fieldName); } else { - s"""has("${gPersistenceBehavior.fieldNameInVertex(fi.get.dataType, fi.get.attrInfo)}")""" + val fName = gPersistenceBehavior.fieldNameInVertex(fi.get.dataType, fi.get.attrInfo) + return GremlinExpressionFactory.INSTANCE.generateUnaryHasExpression(parent, fName); } } } case hasFieldUnaryExpression(fieldName, child) => - s"""${genQuery(child, inSelect)}.has("$fieldName")""" - case ArithmeticExpression(symb, left, right) => s"${genQuery(left, inSelect)} $symb ${genQuery(right, inSelect)}" - case l: Literal[_] => l.toString - case list: ListLiteral[_] => list.toString + val childExpr = genQuery(parent, child, inSelect); + return GremlinExpressionFactory.INSTANCE.generateUnaryHasExpression(childExpr, fieldName); + case ArithmeticExpression(symb, left, right) => { + val leftExpr = genQuery(parent, left, inSelect); + val rightExpr = genQuery(parent, right, inSelect); + return GremlinExpressionFactory.INSTANCE.generateArithmeticExpression(leftExpr, symb, rightExpr); + } + case l: Literal[_] => { + + if(parent != null) { + return new org.apache.atlas.groovy.FieldExpression(parent, cleanStringLiteral(l)); + } + return translateLiteralValue(l.dataType, l); + } + case list: ListLiteral[_] => { + val values : java.util.List[GroovyExpression] = translateList(list.rawValue, false, inSelect); + return new ListExpression(values); + } case in@TraitInstanceExpression(child) => { - val direction = gPersistenceBehavior.traitToInstanceEdgeDirection - s"${genQuery(child, inSelect)}.$direction()" + val childExpr = genQuery(parent, child, inSelect); + val direction = gPersistenceBehavior.traitToInstanceEdgeDirection; + return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(childExpr, direction); } case in@InstanceExpression(child) => { - s"${genQuery(child, inSelect)}" + return genQuery(parent, child, inSelect); } case pe@PathExpression(child) => { - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) { - s"${genQuery(child, inSelect)}.path" - } - else { - s"${genQuery(child, inSelect)}.path()" - } + val childExpr = genQuery(parent, child, inSelect) + return GremlinExpressionFactory.INSTANCE.generatePathExpression(childExpr); } case order@OrderExpression(child, odr, asc) => { var orderby = "" var orderExpression = odr - if(odr.isInstanceOf[BackReference]) { - orderExpression = odr.asInstanceOf[BackReference].reference + if(odr.isInstanceOf[BackReference]) { + orderExpression = odr.asInstanceOf[BackReference].reference } - else if (odr.isInstanceOf[AliasExpression]) { + else if (odr.isInstanceOf[AliasExpression]) { orderExpression = odr.asInstanceOf[AliasExpression].child } - val orderbyProperty = genQuery(orderExpression, false) - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) { - - val bProperty = s"it.b.$orderbyProperty" - val aProperty = s"it.a.$orderbyProperty" - val aCondition = s"($aProperty != null ? $aProperty.toLowerCase(): $aProperty)" - val bCondition = s"($bProperty != null ? $bProperty.toLowerCase(): $bProperty)" - orderby = asc match { - //builds a closure comparison function based on provided order by clause in DSL. This will be used to sort the results by gremlin order pipe. - //Ordering is case insensitive. - case false=> s"order{$bCondition <=> $aCondition}"//descending - case _ => s"order{$aCondition <=> $bCondition}" - } - } - else { - val orderbyProperty = genQuery(orderExpression, true); - val aPropertyExpr = gremlin3ToLowerCase("a"); - val bPropertyExpr = gremlin3ToLowerCase("b"); - - orderby = asc match { - //builds a closure comparison function based on provided order by clause in DSL. This will be used to sort the results by gremlin order pipe. - //Ordering is case insensitive. - case false=> s"""order().by({$orderbyProperty'}, { a,b -> $bPropertyExpr <=> $aPropertyExpr })""" - case _ => s"""order().by({$orderbyProperty},{ a,b -> $aPropertyExpr <=> $bPropertyExpr })""" - } + val childExpr = genQuery(parent, child, inSelect); + var orderByParents : java.util.List[GroovyExpression] = GremlinExpressionFactory.INSTANCE.getOrderFieldParents(); + + val translatedParents : java.util.List[GroovyExpression] = new ArrayList[GroovyExpression](); + var translatedOrderParents = orderByParents.foreach { it => + translatedParents.add(genQuery(it, orderExpression, false)); } - s"""${genQuery(child, inSelect)}.$orderby""" + return GremlinExpressionFactory.INSTANCE.generateOrderByExpression(childExpr, translatedParents,asc); + } case limitOffset@LimitExpression(child, limit, offset) => { - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) { - val totalResultRows = limit.value + offset.value - s"""${genQuery(child, inSelect)} [$offset..<$totalResultRows]""" - } - else { - val totalResultRows = limit.value + offset.value - s"""${genQuery(child, inSelect)}.range($offset,$totalResultRows)""" - } + val childExpr = genQuery(parent, child, inSelect); + val totalResultRows = limit.value + offset.value; + return GremlinExpressionFactory.INSTANCE.generateLimitExpression(childExpr, offset.value, totalResultRows); } case x => throw new GremlinTranslationException(x, "expression not yet supported") } - def gremlin3ToLowerCase(varName : String) : String = { - s"""($varName != null ? $varName.toString().toLowerCase() : null)""" - } - - def genPropertyAccessExpr(e: Option[Expression], fInfo : FieldInfo, quotedPropertyName: String, inSelect: Boolean) : String = { - - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) { - generateAndPrependExpr(e, inSelect, s"""$quotedPropertyName""") - } - else { - val attrInfo : AttributeInfo = fInfo.attrInfo; - val attrType : IDataType[_] = attrInfo.dataType; - if(inSelect) { - val expr = generateAndPrependExpr(e, inSelect, s"""property($quotedPropertyName).orElse(null)"""); - return gPersistenceBehavior.generatePersisentToLogicalConversionExpression(expr, attrType); - } - else { - val unmapped = s"""values($quotedPropertyName)""" - val expr = if(gPersistenceBehavior.isPropertyValueConversionNeeded(attrType)) { - val conversionFunction = gPersistenceBehavior.generatePersisentToLogicalConversionExpression(s"""it.get()""", attrType); - s"""$unmapped.map{ $conversionFunction }""" - } - else { - unmapped - } - generateAndPrependExpr(e, inSelect, expr) - } + def translateList(exprs : List[Expressions.Expression], isAnonymousTraveral: Boolean, inSelect : Boolean) : java.util.List[GroovyExpression] = { + var parent = if(isAnonymousTraveral) {GremlinExpressionFactory.INSTANCE.getAnonymousTraversalExpression() } else { null } + var result : java.util.List[GroovyExpression] = new java.util.ArrayList(exprs.size); + exprs.foreach { it => + result.add(genQuery(parent, it, inSelect)); } + return result; } - def generateAndPrependExpr(e1: Option[Expression], inSelect: Boolean, e2: String) : String = e1 match { + def translateOptChild(parent : GroovyExpression, child : Option[Expressions.Expression] , inSelect: Boolean) : GroovyExpression = child match { - case Some(x) => s"""${genQuery(x, inSelect)}.$e2""" - case None => e2 + case Some(x) => genQuery(parent, x, inSelect) + case None => parent } - def genHasPredicate(e: Option[Expression], fInfo : FieldInfo, fieldName: String, inSelect: Boolean, c: ComparisonExpression, expr: Expression) : String = { + def translateLiteralValue(dataType: IDataType[_], l: Literal[_]): GroovyExpression = { + - val qualifiedPropertyName = s"${gPersistenceBehavior.fieldNameInVertex(fInfo.dataType, fInfo.attrInfo)}" - val persistentExprValue = translateValueToPersistentForm(fInfo, expr); - if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) { - return generateAndPrependExpr(e, inSelect, s"""has("${qualifiedPropertyName}", ${gPersistenceBehavior.gremlinCompOp(c)}, $persistentExprValue)"""); + if (dataType == DataTypes.DATE_TYPE) { + try { + //Accepts both date, datetime formats + val dateStr = cleanStringLiteral(l) + val dateVal = ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(dateStr).getMillis + return new LiteralExpression(dateVal) + } catch { + case pe: java.text.ParseException => + throw new GremlinTranslationException(l, + "Date format " + l + " not supported. Should be of the format " + + TypeSystem.getInstance().getDateFormat.toPattern); + } + } + else if(dataType == DataTypes.BYTE_TYPE) { + //cast needed, otherwise get class cast exception when trying to compare, since the + //persist value is assumed to be an Integer + return new CastExpression(new LiteralExpression(Byte.valueOf(s"""${l}"""), true),"byte"); + } + else if(dataType == DataTypes.INT_TYPE) { + return new LiteralExpression(Integer.valueOf(s"""${l}""")); + } + else if(dataType == DataTypes.BOOLEAN_TYPE) { + return new LiteralExpression(Boolean.valueOf(s"""${l}""")); + } + else if(dataType == DataTypes.SHORT_TYPE) { + return new CastExpression(new LiteralExpression(Short.valueOf(s"""${l}"""), true),"short"); + } + else if(dataType == DataTypes.LONG_TYPE) { + return new LiteralExpression(Long.valueOf(s"""${l}"""), true); + } + else if(dataType == DataTypes.FLOAT_TYPE) { + return new LiteralExpression(Float.valueOf(s"""${l}"""), true); + } + else if(dataType == DataTypes.DOUBLE_TYPE) { + return new LiteralExpression(Double.valueOf(s"""${l}"""), true); + } + else if(dataType == DataTypes.STRING_TYPE) { + return new LiteralExpression(cleanStringLiteral(l)); } else { - val attrInfo : AttributeInfo = fInfo.attrInfo; - val attrType : IDataType[_] = attrInfo.dataType; - if(gPersistenceBehavior.isPropertyValueConversionNeeded(attrType)) { - //for some types, the logical value cannot be stored directly in the underlying graph, - //and conversion logic is needed to convert the persistent form of the value - //to the actual value. In cases like this, we generate a conversion expression to - //do this conversion and use the filter step to perform the comparsion in the gremlin query - val vertexExpr = "((Vertex)it.get())"; - val conversionExpr = gPersistenceBehavior.generatePersisentToLogicalConversionExpression(s"""$vertexExpr.value("$qualifiedPropertyName")""", attrType); - return generateAndPrependExpr(e, inSelect, s"""filter{$vertexExpr.property("$qualifiedPropertyName").isPresent() && $conversionExpr ${gPersistenceBehavior.gremlinPrimitiveOp(c)} $persistentExprValue}"""); - } - else { - return generateAndPrependExpr(e, inSelect, s"""has("${qualifiedPropertyName}", ${gPersistenceBehavior.gremlinCompOp(c)}($persistentExprValue))"""); - } - } - + return new LiteralExpression(l.rawValue); + } } - def translateValueToPersistentForm(fInfo: FieldInfo, l: Expression): Any = { - - val dataType = fInfo.attrInfo.dataType; - val QUOTE = "\""; - - if (dataType == DataTypes.DATE_TYPE) { - try { - //Accepts both date, datetime formats - val dateStr = l.toString.stripPrefix(QUOTE).stripSuffix(QUOTE) - val dateVal = ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(dateStr).getMillis - return dateVal - } catch { - case pe: java.text.ParseException => - throw new GremlinTranslationException(l, - "Date format " + l + " not supported. Should be of the format " + - TypeSystem.getInstance().getDateFormat.toPattern); - } - } - else if(dataType == DataTypes.BYTE_TYPE) { - //cast needed, otherwise get class cast exception when trying to compare, since the - //persist value is assumed to be an Integer - return s"""(byte)$l""" - } - else if(dataType == DataTypes.SHORT_TYPE) { - return s"""(short)$l""" - } - else if(dataType == DataTypes.LONG_TYPE) { - return s"""${l}L""" - } - else if(dataType == DataTypes.FLOAT_TYPE) { - return s"""${l}f""" - } - else if(dataType == DataTypes.DOUBLE_TYPE) { - return s"""${l}d""" - } - else if(dataType == DataTypes.STRING_TYPE) { - return string(escape(l.toString.stripPrefix(QUOTE).stripSuffix(QUOTE))); - } - else { - return l - } - } - def genFullQuery(expr: Expression, hasSelect: Boolean): String = { - - var q = genQuery(expr, false) + + var q : GroovyExpression = new FunctionCallExpression(new IdentifierExpression("g"),"V"); + + val debug:Boolean = false if(gPersistenceBehavior.addGraphVertexPrefix(preStatements)) { - q = s"g.V()${gPersistenceBehavior.initialQueryCondition}.$q" + q = gPersistenceBehavior.addInitialQueryCondition(q); } - q = s"$q.toList()${gPersistenceBehavior.getGraph().getOutputTransformationPredicate(hasSelect, expr.isInstanceOf[PathExpression])}" - + q = genQuery(q, expr, false) + + q = new FunctionCallExpression(q, "toList"); + q = gPersistenceBehavior.getGraph().addOutputTransformationPredicate(q, hasSelect, expr.isInstanceOf[PathExpression]); + + var overallExpression = new CodeBlockExpression(); + overallExpression.addStatements(preStatements); + overallExpression.addStatement(q) + overallExpression.addStatements(postStatements); + + var qryStr = generateGremlin(overallExpression); + if(debug) { - println(" query " + q) + println(" query " + qryStr) } - - q = (preStatements ++ Seq(q) ++ postStatements).mkString("", ";", "") - - /* - * the L:{} represents a groovy code block; the label is needed - * to distinguish it from a groovy closure. - */ - s"L:{$q}" - + + qryStr; + + } + + def generateGremlin(expr: GroovyExpression) : String = { + val ctx : GroovyGenerationContext = new GroovyGenerationContext(); + ctx.setParametersAllowed(false); + expr.generateGroovy(ctx); + return ctx.getQuery; } + def translate(): GremlinQuery = { var e1 = expr.transformUp(wrapAndRule) @@ -570,9 +537,9 @@ class GremlinTranslator(expr: Expression, e1 = e1.transformUp(addAliasToLoopInput()) e1 = e1.transformUp(instanceClauseToTop(e1)) e1 = e1.transformUp(traitClauseWithInstanceForTop(e1)) - + //Following code extracts the select expressions from expression tree. - + val se = SelectExpressionHelper.extractSelectExpression(e1) if (se.isDefined) { val rMap = buildResultMapping(se.get)