/*
 * 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.repository.graphdb.AtlasGraph
import org.apache.atlas.discovery.graph.DefaultGraphPersistenceStrategy
import org.apache.atlas.query.Expressions._
import org.apache.atlas.repository.graph.{AtlasGraphProvider, GraphBackedMetadataRepository}
import org.apache.atlas.typesystem.types.TypeSystem
import org.testng.annotations._
import org.apache.atlas.repository.graph.AtlasGraphProvider
import org.apache.atlas.{DBSandboxer, TestUtils}
import org.apache.atlas.graph.GraphSandboxUtil

class GremlinTest extends BaseGremlinTest {

  var g: AtlasGraph[_,_] = null
  var gp: GraphPersistenceStrategies = null;

  @BeforeMethod
  def resetRequestContext() {
    TestUtils.resetRequestContext()
  }

  @BeforeClass
  def beforeAll() {
     TypeSystem.getInstance().reset()
     var repo = new GraphBackedMetadataRepository(null, new AtlasGraphProvider().get())
     TestUtils.setupGraphProvider(repo)
    //force graph to be initialized first
    AtlasGraphProvider.getGraphInstance()
    
    //create types and indices up front.  Without this, some of the property keys (particularly __traitNames and __superTypes)
    //get ended up created implicitly with some graph backends with the wrong multiplicity.  This also makes the queries
    //we execute perform better :-)    
    QueryTestsUtils.setupTypesAndIndices()    

    gp = new DefaultGraphPersistenceStrategy(repo)
    g = QueryTestsUtils.setupTestGraph(repo)
  }

  @AfterClass
  def afterAll() {
    AtlasGraphProvider.cleanup()
  }


  @Test def testClass {
    val r = QueryProcessor.evaluate(_class("DB"), g, gp)
    validateJson(r, """{
                      |    "query": "DB",
                      |    "dataType": {
                      |        "superTypes": [
                      |
                      |        ],
                      |        "hierarchicalMetaTypeName": "org.apache.atlas.typesystem.types.ClassType",
                      |        "typeName": "DB",
                      |        "attributeDefinitions": [
                      |            {
                      |                "name": "name",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "owner",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "createTime",
                      |                "dataTypeName": "int",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                 },
                      |                "isComposite": false,
                      |               "isUnique": false,
                      |               "isIndexable": false,
                      |               "reverseAttributeName": null
                      |
                      |            },
                      |            {
                      |                "name": "clusterName",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |               "isComposite": false,
                      |               "isUnique": false,
                      |               "isIndexable": false,
                      |               "reverseAttributeName": null
                      |            }
                      |            ]
                      |        },
                      |        "rows": [
                      |            {
                      |                "$typeName$": "DB",
                      |                "$id$": {
                      |                    "$typeName$": "DB",
                      |                    "version": 0
                      |                },
                      |                "owner": "John ETL",
                      |                "name": "Sales",
                      |                "createTime": 1000,
                      |                "clusterName": "test"
                      |            },
                      |            {
                      |                "$typeName$": "DB",
                      |                "$id$": {
                      |                    "$typeName$": "DB",
                      |                    "version": 0
                      |                },
                      |                "owner": "Jane BI",
                      |                "name": "Reporting",
                      |                "createTime": 1500,
                      |                "clusterName": "test"
                      |            }
                      |        ]
                      |    }""".stripMargin)
  }

  @Test def testName {
    val r = QueryProcessor.evaluate(_class("DB").field("name"), g, gp)
    validateJson(r, "{\n  \"query\":\"DB.name\",\n  \"dataType\":\"string\",\n  \"rows\":[\n    \"Sales\",\n    \"Reporting\"\n  ]\n}")
  }

  @Test def testFilter {
    var r = QueryProcessor.evaluate(_class("DB").where(id("name").`=`(string("Reporting"))), g, gp)
    validateJson(r, """{
                      |    "query": "DB where (name = \"Reporting\")",
                      |    "dataType": {
                      |        "superTypes": [],
                      |        "hierarchicalMetaTypeName": "org.apache.atlas.typesystem.types.ClassType",
                      |        "typeName": "DB",
                      |        "attributeDefinitions": [
                      |            {
                      |                "name": "name",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "owner",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "createTime",
                      |                "dataTypeName": "int",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "clusterName",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |               "isComposite": false,
                      |               "isUnique": false,
                      |               "isIndexable": false,
                      |               "reverseAttributeName": null
                      |            }
                      |        ]
                      |    },
                      |    "rows": [
                      |        {
                      |            "$typeName$": "DB",
                      |            "$id$": {
                      |                "$typeName$": "DB",
                      |                "version": 0
                      |            },
                      |            "owner": "Jane BI",
                      |            "name": "Reporting",
                      |            "createTime": 1500,
                      |            "clusterName": "test"
                      |        }
                      |    ]
                      |}""".stripMargin)
  }

  @Test def testFilter2 {
    var r = QueryProcessor.evaluate(_class("DB").where(id("DB").field("name").`=`(string("Reporting"))), g, gp)
    validateJson(r, """{
                      |    "query": "DB where (name = \"Reporting\")",
                      |    "dataType": {
                      |        "superTypes": [],
                      |        "hierarchicalMetaTypeName": "org.apache.atlas.typesystem.types.ClassType",
                      |        "typeName": "DB",
                      |        "attributeDefinitions": [
                      |            {
                      |                "name": "name",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "owner",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "createTime",
                      |                "dataTypeName": "int",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "clusterName",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |               "isComposite": false,
                      |               "isUnique": false,
                      |               "isIndexable": false,
                      |               "reverseAttributeName": null
                      |            }
                      |        ]
                      |    },
                      |    "rows": [
                      |        {
                      |            "$typeName$": "DB",
                      |            "$id$": {
                      |                "$typeName$": "DB",
                      |                "version": 0
                      |            },
                      |            "owner": "Jane BI",
                      |            "name": "Reporting",
                      |            "createTime": 1500,
                      |            "clusterName": "test"
                      |        }
                      |    ]
                      |}""".stripMargin)
  }

  @Test def testSelect {
    val r = QueryProcessor.evaluate(_class("DB").where(id("name").`=`(string("Reporting"))).
      select(id("name"), id("owner")), g, gp)
    validateJson(r, """{
                      |    "query": "DB where (name = \"Reporting\") as _src1 select _src1.name as _src1.name, _src1.owner as _src1.owner",
                      |    "dataType": {
                      |        "typeName": "__tempQueryResultStruct1",
                      |        "attributeDefinitions": [
                      |            {
                      |                "name": "_src1.name",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "_src1.owner",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            }
                      |        ]
                      |    },
                      |    "rows": [
                      |        {
                      |            "$typeName$": "__tempQueryResultStruct1",
                      |            "_src1.owner": "Jane BI",
                      |            "_src1.name": "Reporting"
                      |        }
                      |    ]
                      |}""".stripMargin)
  }

  @Test def testIsTrait {
    val r = QueryProcessor.evaluate(_class("Table").where(isTrait("Dimension")), g, gp)
    validateJson(r, """{
                      |  "query":"Table where Table is Dimension",
                      |  "dataType":{
                      |    "superTypes":[
                      |
                      |    ],
                      |    "hierarchicalMetaTypeName":"org.apache.atlas.typesystem.types.ClassType",
                      |    "typeName":"Table",
                      |    "attributeDefinitions":[
                      |      {
                      |        "name":"name",
                      |        "dataTypeName":"string",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"db",
                      |        "dataTypeName":"DB",
                      |        "multiplicity":{
                      |          "lower":1,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"sd",
                      |        "dataTypeName":"StorageDescriptor",
                      |        "multiplicity":{
                      |          "lower":1,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"created",
                      |        "dataTypeName":"date",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      }
                      |    ]
                      |  },
                      |  "rows":[
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"product_dim",
                      |      "$traits$":{
                      |        "Dimension":{
                      |          "$typeName$":"Dimension"
                      |        }
                      |      }
                      |    },
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"time_dim",
                      |      "$traits$":{
                      |        "Dimension":{
                      |          "$typeName$":"Dimension"
                      |        }
                      |      }
                      |    },
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"customer_dim",
                      |      "$traits$":{
                      |        "Dimension":{
                      |          "$typeName$":"Dimension"
                      |        }
                      |      }
                      |    }
                      |  ]
                      |}""".stripMargin)
  }

  @Test def testhasField {
    val r = QueryProcessor.evaluate(_class("DB").where(hasField("name")), g, gp)
    validateJson(r, """{
                      |  "query":"DB where DB has name",
                      |  "dataType":{
                      |    "superTypes":[
                      |
                      |    ],
                      |    "hierarchicalMetaTypeName":"org.apache.atlas.typesystem.types.ClassType",
                      |    "typeName":"DB",
                      |    "attributeDefinitions":[
                      |      {
                      |        "name":"name",
                      |        "dataTypeName":"string",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"owner",
                      |        "dataTypeName":"string",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"createTime",
                      |        "dataTypeName":"int",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"clusterName",
                      |        "dataTypeName":"string",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      }
                      |    ]
                      |  },
                      |  "rows":[
                      |    {
                      |      "$typeName$":"DB",
                      |      "$id$":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "owner":"John ETL",
                      |      "name":"Sales",
                      |      "createTime":1000,
                      |      "clusterName":"test"
                      |    },
                      |    {
                      |      "$typeName$":"DB",
                      |      "$id$":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "owner":"Jane BI",
                      |      "name":"Reporting",
                      |      "createTime":1500,
                      |      "clusterName":"test"
                      |    }
                      |  ]
                      |}""".stripMargin)
  }

  @Test def testFieldReference {
    val r = QueryProcessor.evaluate(_class("DB").field("Table"), g, gp)
    validateJson(r, """{
                      |  "query":"DB Table",
                      |  "dataType":{
                      |    "superTypes":[      ],
                      |    "hierarchicalMetaTypeName":"org.apache.atlas.typesystem.types.ClassType",
                      |    "typeName":"Table",
                      |    "attributeDefinitions":[
                      |      {
                      |        "name":"name",
                      |        "dataTypeName":"string",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"db",
                      |        "dataTypeName":"DB",
                      |        "multiplicity":{
                      |          "lower":1,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"sd",
                      |        "dataTypeName":"StorageDescriptor",
                      |        "multiplicity":{
                      |          "lower":1,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"created",
                      |        "dataTypeName":"date",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      }
                      |    ]
                      |  },
                      |  "rows":[
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"sales_fact"
                      |    },
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"product_dim",
                      |      "$traits$":{
                      |        "Dimension":{
                      |          "$typeName$":"Dimension"
                      |        }
                      |      }
                      |    },
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"time_dim",
                      |      "$traits$":{
                      |        "Dimension":{
                      |          "$typeName$":"Dimension"
                      |        }
                      |      }
                      |    },
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"customer_dim",
                      |      "$traits$":{
                      |        "Dimension":{
                      |          "$typeName$":"Dimension"
                      |        }
                      |      }
                      |    },
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"sales_fact_daily_mv"
                      |    },
                      |    {
                      |      "$typeName$":"Table",
                      |      "$id$":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      },
                      |      "created":"2014-12-11T02:35:58.440Z",
                      |      "sd":{
                      |        "$typeName$":"StorageDescriptor",
                      |        "version":0
                      |      },
                      |      "db":{
                      |        "$typeName$":"DB",
                      |        "version":0
                      |      },
                      |      "name":"sales_fact_monthly_mv"
                      |    }
                      |  ]
                      |}""".stripMargin)
  }

  @Test def testBackReference {
    val r = QueryProcessor.evaluate(
      _class("DB").as("db").field("Table").where(id("db").field("name").`=`(string("Reporting"))), g, gp)
    validateJson(r, null)
  }

  @Test def testArith {
    val r = QueryProcessor.evaluate(_class("DB").where(id("name").`=`(string("Reporting"))).
      select(id("name"), id("createTime") + int(1)), g, gp)
    validateJson(r, "{\n  \"query\":\"DB where (name = \\\"Reporting\\\") as _src1 select _src1.name as _src1.name, (_src1.createTime + 1) as (_src1.createTime + 1)\",\n  \"dataType\":{\n    \"typeName\":\"__tempQueryResultStruct3\",\n    \"attributeDefinitions\":[\n      {\n        \"name\":\"_src1.name\",\n        \"dataTypeName\":\"string\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      },\n      {\n        \"name\":\"(_src1.createTime + 1)\",\n        \"dataTypeName\":\"int\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      }\n    ]\n  },\n  \"rows\":[\n    {\n      \"$typeName$\":\"__tempQueryResultStruct3\",\n      \"(_src1.createTime + 1)\":1501,\n      \"_src1.name\":\"Reporting\"\n    }\n  ]\n}")
  }

  @Test def testComparisonLogical {
    val r = QueryProcessor.evaluate(_class("DB").where(id("name").`=`(string("Reporting")).
      and(id("createTime") > int(0))), g, gp)
    validateJson(r, """{
                      |    "query": "DB where (name = \"Reporting\") and (createTime > 0)",
                      |    "dataType": {
                      |        "superTypes": [
                      |
                      |        ],
                      |        "hierarchicalMetaTypeName": "org.apache.atlas.typesystem.types.ClassType",
                      |        "typeName": "DB",
                      |        "attributeDefinitions": [
                      |            {
                      |                "name": "name",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "owner",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "createTime",
                      |                "dataTypeName": "int",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |                "isComposite": false,
                      |                "isUnique": false,
                      |                "isIndexable": false,
                      |                "reverseAttributeName": null
                      |            },
                      |            {
                      |                "name": "clusterName",
                      |                "dataTypeName": "string",
                      |                "multiplicity": {
                      |                    "lower": 0,
                      |                    "upper": 1,
                      |                    "isUnique": false
                      |                },
                      |               "isComposite": false,
                      |               "isUnique": false,
                      |               "isIndexable": false,
                      |               "reverseAttributeName": null
                      |            }
                      |        ]
                      |    },
                      |    "rows": [
                      |        {
                      |            "$typeName$": "DB",
                      |            "$id$": {
                      |                "$typeName$": "DB",
                      |                "version": 0
                      |            },
                      |            "owner": "Jane BI",
                      |            "name": "Reporting",
                      |            "createTime": 1500,
                      |            "clusterName": "test"
                      |        }
                      |    ]
                      |}""".stripMargin)
  }

  @Test def testJoinAndSelect1 {
    val r = QueryProcessor.evaluate(
      _class("DB").as("db1").where(id("name").`=`(string("Sales"))).field("Table").as("tab").
        where(isTrait("Dimension")).
        select(id("db1").field("name").as("dbName"), id("tab").field("name").as("tabName")), g, gp
    )
    validateJson(r, "{\n  \"query\":\"DB as db1 where (name = \\\"Sales\\\") Table as tab where DB as db1 where (name = \\\"Sales\\\") Table as tab is Dimension as _src1 select db1.name as dbName, tab.name as tabName\",\n  \"dataType\":{\n    \"typeName\":\"__tempQueryResultStruct5\",\n    \"attributeDefinitions\":[\n      {\n        \"name\":\"dbName\",\n        \"dataTypeName\":\"string\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      },\n      {\n        \"name\":\"tabName\",\n        \"dataTypeName\":\"string\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      }\n    ]\n  },\n  \"rows\":[\n    {\n      \"$typeName$\":\"__tempQueryResultStruct5\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"product_dim\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct5\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"time_dim\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct5\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"customer_dim\"\n    }\n  ]\n}")
  }

  @Test def testJoinAndSelect2 {
    val r = QueryProcessor.evaluate(
      _class("DB").as("db1").where((id("db1").field("createTime") > int(0))
        .or(id("name").`=`(string("Reporting")))).field("Table").as("tab")
        .select(id("db1").field("name").as("dbName"), id("tab").field("name").as("tabName")), g, gp
    )
    validateJson(r, "{\n  \"query\":\"DB as db1 where (createTime > 0) or (name = \\\"Reporting\\\") Table as tab select db1.name as dbName, tab.name as tabName\",\n  \"dataType\":{\n    \"typeName\":\"__tempQueryResultStruct6\",\n    \"attributeDefinitions\":[\n      {\n        \"name\":\"dbName\",\n        \"dataTypeName\":\"string\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      },\n      {\n        \"name\":\"tabName\",\n        \"dataTypeName\":\"string\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      }\n    ]\n  },\n  \"rows\":[\n    {\n      \"$typeName$\":\"__tempQueryResultStruct6\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"sales_fact\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct6\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"product_dim\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct6\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"time_dim\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct6\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"customer_dim\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct6\",\n      \"dbName\":\"Reporting\",\n      \"tabName\":\"sales_fact_daily_mv\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct6\",\n      \"dbName\":\"Reporting\",\n      \"tabName\":\"sales_fact_monthly_mv\"\n    }\n  ]\n}")
  }

  @Test def testJoinAndSelect3 {
    val r = QueryProcessor.evaluate(
      _class("DB").as("db1").where((id("db1").field("createTime") > int(0))
        .and(id("db1").field("name").`=`(string("Reporting")))
        .or(id("db1").hasField("owner"))).field("Table").as("tab")
        .select(id("db1").field("name").as("dbName"), id("tab").field("name").as("tabName")), g, gp
    )
    validateJson(r, "{\n  \"query\":\"DB as db1 where (createTime > 0) and (name = \\\"Reporting\\\") or db1 has owner Table as tab select db1.name as dbName, tab.name as tabName\",\n  \"dataType\":{\n    \"typeName\":\"__tempQueryResultStruct7\",\n    \"attributeDefinitions\":[\n      {\n        \"name\":\"dbName\",\n        \"dataTypeName\":\"string\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      },\n      {\n        \"name\":\"tabName\",\n        \"dataTypeName\":\"string\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      }\n    ]\n  },\n  \"rows\":[\n    {\n      \"$typeName$\":\"__tempQueryResultStruct7\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"sales_fact\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct7\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"product_dim\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct7\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"time_dim\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct7\",\n      \"dbName\":\"Sales\",\n      \"tabName\":\"customer_dim\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct7\",\n      \"dbName\":\"Reporting\",\n      \"tabName\":\"sales_fact_daily_mv\"\n    },\n    {\n      \"$typeName$\":\"__tempQueryResultStruct7\",\n      \"dbName\":\"Reporting\",\n      \"tabName\":\"sales_fact_monthly_mv\"\n    }\n  ]\n}")
  }

  @Test def testJoinAndSelect4 {
    val r = QueryProcessor.evaluate(
      _class("DB").as("db1").where(id("name").`=`(string("Sales"))).field("Table").as("tab").
        where(isTrait("Dimension")).
        select(id("db1").as("dbO"), id("tab").field("name").as("tabName")), g, gp
    )
    validateJson(r, "{\n  \"query\":\"DB as db1 where (name = \\\"Sales\\\") Table as tab where DB as db1 where (name = \\\"Sales\\\") Table as tab is Dimension as _src1 select db1 as dbO, tab.name as tabName\",\n  \"dataType\":{\n    \"typeName\":\"\",\n    \"attributeDefinitions\":[\n      {\n        \"name\":\"dbO\",\n        \"dataTypeName\":\"DB\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      },\n      {\n        \"name\":\"tabName\",\n        \"dataTypeName\":\"string\",\n        \"multiplicity\":{\n          \"lower\":0,\n          \"upper\":1,\n          \"isUnique\":false\n        },\n        \"isComposite\":false,\n        \"isUnique\":false,\n        \"isIndexable\":false,\n        \"reverseAttributeName\":null\n      }\n    ]\n  },\n  \"rows\":[\n    {\n      \"$typeName$\":\"\",\n      \"dbO\":{\n        \"$typeName$\":\"DB\",\n        \"version\":0\n      },\n      \"tabName\":\"product_dim\"\n    },\n    {\n      \"$typeName$\":\"\",\n      \"dbO\":{\n        \"$typeName$\":\"DB\",\n        \"version\":0\n      },\n      \"tabName\":\"time_dim\"\n    },\n    {\n      \"$typeName$\":\"\",\n      \"dbO\":{\n        \"$typeName$\":\"DB\",\n        \"version\":0\n      },\n      \"tabName\":\"customer_dim\"\n    }\n  ]\n}")
  }

  @Test def testArrayComparision {
    val p = QueryParser
    val e = p("Partition as p where values = ['2015-01-01']," +
      " table where name = 'sales_fact_daily_mv'," +
      " db where name = 'Reporting' and clusterName = 'test' select p").right.get
    val r = QueryProcessor.evaluate(e, g, gp)
    validateJson(r, """{
                      |  "query":"Partition as p where (values = [\"2015-01-01\"]) table where (name = \"sales_fact_daily_mv\") db where (name = \"Reporting\") and (clusterName = \"test\") as _src1 select p as p",
                      |  "dataType":{
                      |    "typeName":"__tempQueryResultStruct2",
                      |    "attributeDefinitions":[
                      |      {
                      |        "name":"p",
                      |        "dataTypeName":"Partition",
                      |        "multiplicity":{
                      |          "lower":0,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      }
                      |    ]
                      |  },
                      |  "rows":[
                      |    {
                      |      "$typeName$":"__tempQueryResultStruct2",
                      |      "p":{
                      |        "$typeName$":"Partition",
                      |        "version":0
                      |      }
                      |    }
                      |  ]
                      |}""".stripMargin)
  }

  @Test def testArrayComparisionWithSelectOnArray {
    val p = QueryParser
    val e = p("Partition as p where values = ['2015-01-01']," +
      " table where name = 'sales_fact_daily_mv'," +
      " db where name = 'Reporting' and clusterName = 'test' select p.values").right.get
    val r = QueryProcessor.evaluate(e, g, gp)
    validateJson(r,
      """{
        |  "query":"Partition as p where (values = [\"2015-01-01\"]) table where (name = \"sales_fact_daily_mv\") db where (name = \"Reporting\") and (clusterName = \"test\") as _src1 select p.values as p.values",
        |  "dataType":{
        |    "typeName":"__tempQueryResultStruct2",
        |    "attributeDefinitions":[
        |  {
        |    "name":"p.values",
        |    "dataTypeName":"array<string>",
        |    "multiplicity":{
        |    "lower":0,
        |    "upper":1,
        |    "isUnique":false
        |  },
        |    "isComposite":false,
        |    "isUnique":false,
        |    "isIndexable":false,
        |    "reverseAttributeName":null
        |  }
        |    ]
        |  },
        |  "rows":[
        |  {
        |    "$typeName$":"__tempQueryResultStruct2",
        |    "p.values":[
        |    "2015-01-01"
        |    ]
        |  }
        |  ]
        |}
      """.stripMargin)
  }

  @Test def testArrayInWhereClause {
    val p = QueryParser
    val e = p("Partition as p where values = ['2015-01-01']").right.get
    val r = QueryProcessor.evaluate(e, g, gp)
    validateJson(r, """{
                      |  "query":"Partition as p where (values = [\"2015-01-01\"])",
                      |  "dataType":{
                      |    "superTypes":[
                      |
                      |    ],
                      |    "hierarchicalMetaTypeName":"org.apache.atlas.typesystem.types.ClassType",
                      |    "typeName":"Partition",
                      |    "attributeDefinitions":[
                      |      {
                      |        "name":"values",
                      |        "dataTypeName":"array<string>",
                      |        "multiplicity":{
                      |          "lower":1,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      },
                      |      {
                      |        "name":"table",
                      |        "dataTypeName":"Table",
                      |        "multiplicity":{
                      |          "lower":1,
                      |          "upper":1,
                      |          "isUnique":false
                      |        },
                      |        "isComposite":false,
                      |        "isUnique":false,
                      |        "isIndexable":false,
                      |        "reverseAttributeName":null
                      |      }
                      |    ]
                      |  },
                      |  "rows":[
                      |    {
                      |      "$typeName$":"Partition",
                      |      "$id$":{
                      |        "$typeName$":"Partition",
                      |        "version":0
                      |      },
                      |      "values":[
                      |        "2015-01-01"
                      |      ],
                      |      "table":{
                      |        "$typeName$":"Table",
                      |        "version":0
                      |      }
                      |    }
                      |  ]
                      |}""".stripMargin)
  }

  @Test def testArrayWithStruct {
//    val p = new QueryParser
//    val e = p("from LoadProcess select inputTables").right.get
//    val r = QueryProcessor.evaluate(e, g)
    val r = QueryProcessor.evaluate(_class("LoadProcess").field("inputTables"), g, gp)
    validateJson(r)
  }

  @Test(expectedExceptions =  Array(classOf[ExpressionException]))
  def testNegativeInvalidType {
    val p = QueryParser
    val e = p("from blah").right.get
    QueryProcessor.evaluate(e, g, gp)
  }

  @Test def testJoinAndSelect5 {
    val p = QueryParser
    val e = p("Table as t where name = 'sales_fact' db where name = 'Sales' and owner = 'John ETL' select t").right.get
    val r = QueryProcessor.evaluate(e, g, gp)
    validateJson(r)
  }
}
