blob: 5f8ef8ae28072cedd5ba6abe08b31a84d56b2789 [file] [log] [blame]
/*
* 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.query
import org.apache.atlas.query.Expressions._
import org.apache.atlas.query.TypeUtils.ResultWithPathStruct
import org.apache.atlas.repository.graphdb.AtlasGraph
import org.apache.atlas.typesystem.json._
import org.apache.atlas.typesystem.types.DataTypes.TypeCategory
import org.apache.atlas.typesystem.types._
import org.json4s._
import org.json4s.native.Serialization._
import scala.language.existentials
case class GremlinQueryResult(query: String,
resultDataType: IDataType[_],
rows: java.util.List[_]) {
def toJson = JsonHelper.toJson(this)
}
class GremlinEvaluator(qry: GremlinQuery, persistenceStrategy: GraphPersistenceStrategies, g: AtlasGraph[_,_]) {
/**
*
* @param gResultObj is the object returned from gremlin. This must be a List
* @param qryResultObj is the object constructed for the output w/o the Path.
* @return a ResultWithPathStruct
*/
def addPathStruct(gResultObj: AnyRef, qryResultObj: Any): Any = {
if (!qry.isPathExpression) {
qryResultObj
} else {
import scala.collection.JavaConversions._
import scala.collection.JavaConverters._
val iPaths = gResultObj.asInstanceOf[java.util.List[AnyRef]].init
val oPaths = iPaths.map { value =>
persistenceStrategy.constructInstance(TypeSystem.getInstance().getIdType.getStructType, value)
}.toList.asJava
val sType = qry.expr.dataType.asInstanceOf[StructType]
val sInstance = sType.createInstance()
sInstance.set(ResultWithPathStruct.pathAttrName, oPaths)
sInstance.set(ResultWithPathStruct.resultAttrName, qryResultObj)
sInstance
}
}
def instanceObject(v: AnyRef): AnyRef = {
if (qry.isPathExpression) {
import scala.collection.JavaConversions._
v.asInstanceOf[java.util.List[AnyRef]].last
} else {
v
}
}
def evaluate(): GremlinQueryResult = {
import scala.collection.JavaConversions._
val debug:Boolean = false
val rType = qry.expr.dataType
val oType = if (qry.isPathExpression) {
qry.expr.children(0).dataType
}
else {
rType
}
val rawRes = g.executeGremlinScript(qry.queryStr, qry.isPathExpression);
if(debug) {
println(" rawRes " +rawRes)
}
if (!qry.hasSelectList && ! qry.isGroupBy) {
val rows = rawRes.asInstanceOf[java.util.List[AnyRef]].map { v =>
val instObj = instanceObject(v)
val o = persistenceStrategy.constructInstance(oType, instObj)
addPathStruct(v, o)
}
GremlinQueryResult(qry.expr.toString, rType, rows.toList)
} else {
val sType = oType.asInstanceOf[StructType]
val rows = rawRes.asInstanceOf[java.util.List[AnyRef]].map { r =>
val rV = instanceObject(r)
val sInstance = sType.createInstance()
val selObj = SelectExpressionHelper.extractSelectExpression(qry.expr)
if (selObj.isDefined) {
val selExpr = selObj.get.asInstanceOf[Expressions.SelectExpression]
selExpr.selectListWithAlias.foreach { aE =>
val cName = aE.alias
val (src, idx) = qry.resultMaping(cName)
val v = getColumnValue(rV, src, idx)
//if select clause is selecting the entire object then return only the instance id (guid, version, state and typeName)
if (aE.dataType.getTypeCategory == TypeCategory.CLASS) {
sInstance.set(cName, persistenceStrategy.constructClassInstanceId(aE.dataType.asInstanceOf[ClassType], v))
} else {
sInstance.set(cName, persistenceStrategy.constructInstance(aE.dataType, v))
}
}
}
else if(qry.isGroupBy) {
//the order in the result will always match the order in the select list
val selExpr = qry.expr.asInstanceOf[GroupByExpression].selExpr
var idx = 0;
val row : java.util.List[Object] = rV.asInstanceOf[java.util.List[Object]]
selExpr.selectListWithAlias.foreach { aE =>
val cName = aE.alias
val cValue = row.get(idx);
sInstance.set(cName, persistenceStrategy.constructInstance(aE.dataType, cValue))
idx += 1;
}
}
addPathStruct(r, sInstance)
}
GremlinQueryResult(qry.expr.toString, rType, rows.toList)
}
}
private def getColumnValue(rowValue: AnyRef, colName: String, idx: Integer) : AnyRef = {
var rawColumnValue: AnyRef = null;
if(rowValue.isInstanceOf[java.util.Map[_,_]]) {
val columnsMap = rowValue.asInstanceOf[java.util.Map[String,AnyRef]];
rawColumnValue = columnsMap.get(colName);
}
else {
//when there is only one column, result does not come back as a map
rawColumnValue = rowValue;
}
var value : AnyRef = null;
if(rawColumnValue.isInstanceOf[java.util.List[_]] && idx >= 0) {
val arr = rawColumnValue.asInstanceOf[java.util.List[AnyRef]];
value = arr.get(idx);
}
else {
value = rawColumnValue;
}
return value;
}
}
object JsonHelper {
class GremlinQueryResultSerializer()
extends Serializer[GremlinQueryResult] {
def deserialize(implicit format: Formats) = {
throw new UnsupportedOperationException("Deserialization of GremlinQueryResult not supported")
}
def serialize(implicit f: Formats) = {
case GremlinQueryResult(query, rT, rows) =>
JObject(JField("query", JString(query)),
JField("dataType", TypesSerialization.toJsonValue(rT)(f)),
JField("rows", Extraction.decompose(rows)(f))
)
}
}
implicit val formats = org.json4s.native.Serialization.formats(NoTypeHints) + new TypedStructSerializer +
new TypedReferenceableInstanceSerializer + new BigDecimalSerializer + new BigIntegerSerializer +
new GremlinQueryResultSerializer
def toJson(r: GremlinQueryResult): String = {
writePretty(r)
}
}