Commit 3718af6b by Harish Butani

add support for instance and traitInstance expressions

parent 305d16a1
......@@ -322,6 +322,8 @@ object Expressions {
def loop(loopingExpr: Expression, times: Literal[Integer]) =
new LoopExpression(this, loopingExpr, Some(times))
def traitInstance() = new TraitInstanceExpression(this)
def instance() = new InstanceExpression(this)
}
trait BinaryNode {
......@@ -405,6 +407,7 @@ object Expressions {
}
val children = if (child.isDefined) List(child.get) else Nil
import scala.language.existentials
lazy val dataType = {
val t = {
if (fieldInfo.traitName != null ) {
......@@ -688,4 +691,34 @@ object Expressions {
else s"$input loop ($loopingExpression)"
}
}
case class TraitInstanceExpression(child: Expression)
extends Expression with UnaryNode {
lazy val dataType = {
if (!resolved) {
throw new UnresolvedException(this,
s"datatype. Can not resolve due to unresolved child")
}
if (!child.dataType.isInstanceOf[TraitType]) {
throw new ExpressionException(this,
s"Cannot apply instance on ${child.dataType.getName}, it is not a TraitType")
}
TypeUtils.INSTANCE_ID_TYP
}
override def toString = s"$child traitInstance"
}
case class InstanceExpression(child: Expression)
extends Expression with UnaryNode {
lazy val dataType = {
if (!resolved) {
throw new UnresolvedException(this,
s"datatype. Can not resolve due to unresolved child")
}
TypeUtils.INSTANCE_ID_TYP
}
override def toString = s"$child instance"
}
}
......@@ -157,6 +157,30 @@ class GremlinTranslator(expr: Expression,
}
}
def instanceClauseToTop(topE : Expression) : PartialFunction[Expression, Expression] = {
case le : LogicalExpression if (le fastEquals topE) => {
le.instance()
}
case ce : ComparisonExpression if (ce fastEquals topE) => {
ce.instance()
}
case he : hasFieldUnaryExpression if (he fastEquals topE) => {
he.instance()
}
}
def traitClauseWithInstanceForTop(topE : Expression) : PartialFunction[Expression, Expression] = {
case te : TraitExpression if (te fastEquals topE) => {
val theTrait = te.as("theTrait")
val theInstance = theTrait.traitInstance().as("theInstance")
val outE =
theInstance.select(id("theTrait"),
id("theInstance").field(TypeUtils.INSTANCE_ID_TYP_TYPENAME_ATTRNAME).as("instanceTypeName"),
id("theInstance").field(TypeUtils.INSTANCE_ID_TYP_ID_ATTRNAME).as("instanceId"))
QueryProcessor.validate(outE)
}
}
private def genQuery(expr: Expression, inSelect: Boolean): String = expr match {
case ClassExpression(clsName) => s"""has("${gPersistenceBehavior.typeAttributeName}","$clsName")"""
case TraitExpression(clsName) => s"""has("${gPersistenceBehavior.typeAttributeName}","$clsName")"""
......@@ -178,7 +202,7 @@ class GremlinTranslator(expr: Expression,
}
case fe@FieldExpression(fieldName, fInfo, child)
if fInfo.traitName != null => {
val direction = "out"
val direction = gPersistenceBehavior.instanceToTraitEdgeDirection
val edgeLbl = gPersistenceBehavior.edgeLabel(fInfo)
val step = s"""$direction("$edgeLbl")"""
child match {
......@@ -239,6 +263,13 @@ class GremlinTranslator(expr: Expression,
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 in@TraitInstanceExpression(child) => {
val direction = gPersistenceBehavior.traitToInstanceEdgeDirection
s"${genQuery(child, inSelect)}.$direction()"
}
case in@InstanceExpression(child) => {
s"${genQuery(child, inSelect)}"
}
case x => throw new GremlinTranslationException(x, "expression not yet supported")
}
......@@ -250,6 +281,8 @@ class GremlinTranslator(expr: Expression,
e1 = e1.transformUp(new AddAliasToSelectInput)
e1.traverseUp(validateSelectExprHaveOneSrc)
e1 = e1.transformUp(addAliasToLoopInput())
e1 = e1.transformUp(instanceClauseToTop(e1))
e1 = e1.transformUp(traitClauseWithInstanceForTop(e1))
e1 match {
case e1: SelectExpression => {
......
......@@ -27,7 +27,7 @@ import Matchers._
import org.scalatest.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class GremlinTest extends FunSuite with BeforeAndAfterAll {
class GremlinTest extends FunSuite with BeforeAndAfterAll with BaseGremlinTest {
var g: TitanGraph = null
......@@ -41,19 +41,6 @@ class GremlinTest extends FunSuite with BeforeAndAfterAll {
g.shutdown()
}
val STRUCT_NAME_REGEX = (TypeUtils.TEMP_STRUCT_NAME_PREFIX + "\\d+").r
def validateJson(r: GremlinQueryResult, expected: String = null): Unit = {
val rJ = r.toJson
if (expected != null) {
val a = STRUCT_NAME_REGEX.replaceAllIn(rJ, "")
val b = STRUCT_NAME_REGEX.replaceAllIn(expected, "")
Assertions.assert(a == b)
} else {
println(rJ)
}
}
test("testClass") {
val r = QueryProcessor.evaluate(_class("DB"), g)
validateJson(r, "{\n \"query\":\"DB\",\n \"dataType\":{\n \"superTypes\":[\n \n ],\n \"hierarchicalMetaTypeName\":\"org.apache.hadoop.metadata.typesystem.types.ClassType\",\n \"typeName\":\"DB\",\n \"attributeDefinitions\":[\n {\n \"name\":\"name\",\n \"dataTypeName\":\"string\",\n \"multiplicity\":{\n \"lower\":0,\n \"upper\":1,\n \"isUnique\":false\n },\n \"isComposite\":false,\n \"isUnique\":false,\n \"isIndexable\":true,\n \"reverseAttributeName\":null\n },\n {\n \"name\":\"owner\",\n \"dataTypeName\":\"string\",\n \"multiplicity\":{\n \"lower\":0,\n \"upper\":1,\n \"isUnique\":false\n },\n \"isComposite\":false,\n \"isUnique\":false,\n \"isIndexable\":true,\n \"reverseAttributeName\":null\n },\n {\n \"name\":\"createTime\",\n \"dataTypeName\":\"int\",\n \"multiplicity\":{\n \"lower\":0,\n \"upper\":1,\n \"isUnique\":false\n },\n \"isComposite\":false,\n \"isUnique\":false,\n \"isIndexable\":true,\n \"reverseAttributeName\":null\n }\n ]\n },\n \"rows\":[\n {\n \"$typeName$\":\"DB\",\n \"$id$\":{\n \"id\":\"256\",\n \"$typeName$\":\"DB\",\n \"version\":0\n },\n \"owner\":\"John ETL\",\n \"name\":\"Sales\",\n \"createTime\":1000\n },\n {\n \"$typeName$\":\"DB\",\n \"$id$\":{\n \"id\":\"7168\",\n \"$typeName$\":\"DB\",\n \"version\":0\n },\n \"owner\":\"Jane BI\",\n \"name\":\"Reporting\",\n \"createTime\":1500\n }\n ]\n}")
......
......@@ -27,7 +27,7 @@ import Matchers._
import org.scalatest.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class GremlinTest2 extends FunSuite with BeforeAndAfterAll {
class GremlinTest2 extends FunSuite with BeforeAndAfterAll with BaseGremlinTest {
var g: TitanGraph = null
......@@ -41,22 +41,29 @@ class GremlinTest2 extends FunSuite with BeforeAndAfterAll {
g.shutdown()
}
val STRUCT_NAME_REGEX = (TypeUtils.TEMP_STRUCT_NAME_PREFIX + "\\d+").r
def validateJson(r: GremlinQueryResult, expected: String = null): Unit = {
val rJ = r.toJson
if (expected != null) {
val a = STRUCT_NAME_REGEX.replaceAllIn(rJ, "")
val b = STRUCT_NAME_REGEX.replaceAllIn(expected, "")
Assertions.assert(a == b)
} else {
println(rJ)
}
}
test("testTraitSelect") {
val r = QueryProcessor.evaluate(_class("Table").as("t").join("Dimension").as("dim").select(id("t"), id("dim")), g)
validateJson(r, "{\n \"query\":\"Table as t.Dimension as dim select t as _col_0, dim as _col_1\",\n \"dataType\":{\n \"typeName\":\"\",\n \"attributeDefinitions\":[\n {\n \"name\":\"_col_0\",\n \"dataTypeName\":\"Table\",\n \"multiplicity\":{\n \"lower\":0,\n \"upper\":1,\n \"isUnique\":false\n },\n \"isComposite\":false,\n \"isUnique\":false,\n \"isIndexable\":true,\n \"reverseAttributeName\":null\n },\n {\n \"name\":\"_col_1\",\n \"dataTypeName\":\"Dimension\",\n \"multiplicity\":{\n \"lower\":0,\n \"upper\":1,\n \"isUnique\":false\n },\n \"isComposite\":false,\n \"isUnique\":false,\n \"isIndexable\":true,\n \"reverseAttributeName\":null\n }\n ]\n },\n \"rows\":[\n {\n \"$typeName$\":\"\",\n \"_col_1\":{\n \"$typeName$\":\"Dimension\"\n },\n \"_col_0\":{\n \"id\":\"3328\",\n \"$typeName$\":\"Table\",\n \"version\":0\n }\n },\n {\n \"$typeName$\":\"\",\n \"_col_1\":{\n \"$typeName$\":\"Dimension\"\n },\n \"_col_0\":{\n \"id\":\"4864\",\n \"$typeName$\":\"Table\",\n \"version\":0\n }\n },\n {\n \"$typeName$\":\"\",\n \"_col_1\":{\n \"$typeName$\":\"Dimension\"\n },\n \"_col_0\":{\n \"id\":\"6656\",\n \"$typeName$\":\"Table\",\n \"version\":0\n }\n }\n ]\n}")
}
test("testTrait") {
val r = QueryProcessor.evaluate(_trait("Dimension"), g)
validateJson(r)
}
test("testTraitInstance") {
val r = QueryProcessor.evaluate(_trait("Dimension").traitInstance(), g)
validateJson(r)
}
test("testInstanceAddedToFilter") {
val r = QueryProcessor.evaluate(_trait("Dimension").hasField("typeName"), g)
validateJson(r)
}
test("testInstanceFilter") {
val r = QueryProcessor.evaluate(_trait("Dimension").traitInstance().hasField("name"), g)
validateJson(r)
}
}
\ No newline at end of file
......@@ -28,6 +28,7 @@ import com.typesafe.config.{Config, ConfigFactory}
import org.apache.commons.configuration.{Configuration, ConfigurationException, MapConfiguration}
import org.apache.commons.io.FileUtils
import org.apache.hadoop.metadata.typesystem.types._
import org.scalatest.{BeforeAndAfterAll, FunSuite, Assertions}
trait GraphUtils {
......@@ -147,4 +148,22 @@ object QueryTestsUtils extends GraphUtils {
engine.eval("g.loadGraphSON(hiveGraphFile)", bindings)
g
}
}
trait BaseGremlinTest {
self : FunSuite with BeforeAndAfterAll =>
val STRUCT_NAME_REGEX = (TypeUtils.TEMP_STRUCT_NAME_PREFIX + "\\d+").r
def validateJson(r: GremlinQueryResult, expected: String = null): Unit = {
val rJ = r.toJson
if (expected != null) {
val a = STRUCT_NAME_REGEX.replaceAllIn(rJ, "")
val b = STRUCT_NAME_REGEX.replaceAllIn(expected, "")
Assertions.assert(a == b)
} else {
println(rJ)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment