blob: 0ff4db1cf036c983ff607b86114fbabf2b16a141 [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.s2graph.core.parsers
import org.apache.s2graph.core._
import org.apache.s2graph.core.schema.{ServiceColumn, Label, LabelMeta}
import org.apache.s2graph.core.rest.TemplateHelper
import org.apache.s2graph.core.types._
import org.apache.s2graph.core.utils.logger
import org.scalatest.{FunSuite, Matchers}
import play.api.libs.json.Json
import scala.util.{Random, Try}
class WhereParserTest extends FunSuite with Matchers with TestCommonWithModels {
initTests()
import HBaseType._
val ts = System.currentTimeMillis()
val dummyTs = LabelMeta.timestamp -> InnerValLikeWithTs.withLong(ts, ts, label.schemaVersion)
def validate(label: Label)(edge: S2EdgeLike)(sql: String)(expected: Boolean) = {
def debug(whereOpt: Try[Where]) = {
println("==================")
println(s"$whereOpt")
println(s"$edge")
println("==================")
}
val whereOpt = WhereParser().parse(sql)
if (whereOpt.isFailure) {
debug(whereOpt)
whereOpt.get // touch exception
} else {
val ret = whereOpt.get.filter(edge)
if (ret != expected) {
debug(whereOpt)
}
ret shouldBe expected
}
}
def ids = for {
version <- ValidVersions
} yield {
val srcId = SourceVertexId(ServiceColumn.Default, InnerVal.withLong(1, version))
val tgtId =
if (version == VERSION2) TargetVertexId(ServiceColumn.Default, InnerVal.withStr("2", version))
else TargetVertexId(ServiceColumn.Default, InnerVal.withLong(2, version))
val srcVertex = builder.newVertex(srcId, ts)
val tgtVertex = builder.newVertex(tgtId, ts)
val (_label, dir) = if (version == VERSION2) (labelV2, labelWithDirV2.dir) else (label, labelWithDir.dir)
(srcVertex, tgtVertex, _label, dir)
}
test("check where clause not nested") {
for {
(srcVertex, tgtVertex, label, dir) <- ids
} {
/** test for each version */
val js = Json.obj("is_hidden" -> true, "is_blocked" -> false, "weight" -> 10, "time" -> 3, "phone_number" -> "1234")
val propsInner = Management.toProps(label, js.fields).map { case (k, v) => k -> InnerValLikeWithTs(v, ts) }.toMap + dummyTs
val edge = builder.newEdge(srcVertex, tgtVertex, label, dir, 0.toByte, ts, propsInner)
val f = validate(label)(edge) _
/** labelName label is long-long relation */
f(s"_to=${tgtVertex.innerId}")(true)
f(s"_to=19230495")(false)
f(s"_to!=19230495")(true)
f(s"phone_number=1234")(true)
}
}
test("check where clause with string literal") {
for {
(srcVertex, tgtVertex,
label, dir) <- ids
} {
/** test for each version */
var js = Json.obj("phone_number" -> "")
var propsInner = Management.toProps(label, js.fields).map { case (k, v) => k -> InnerValLikeWithTs(v, ts) }.toMap + dummyTs
var edge = builder.newEdge(srcVertex, tgtVertex, label, labelWithDir.dir, 0.toByte, ts, propsInner)
var f = validate(label)(edge) _
f(s"phone_number = '' ")(true)
js = Json.obj("phone_number" -> "010 3167 1897")
propsInner = Management.toProps(label, js.fields).map { case (k, v) => k -> InnerValLikeWithTs(v, ts) }.toMap + dummyTs
edge = builder.newEdge(srcVertex, tgtVertex, label, labelWithDir.dir, 0.toByte, ts, propsInner)
f = validate(label)(edge) _
f(s"phone_number = '010 3167 1897' ")(true)
js = Json.obj("phone_number" -> "010' 3167 1897")
propsInner = Management.toProps(label, js.fields).map { case (k, v) => k -> InnerValLikeWithTs(v, ts) }.toMap + dummyTs
edge = builder.newEdge(srcVertex, tgtVertex, label, labelWithDir.dir, 0.toByte, ts, propsInner)
f = validate(label)(edge) _
f(s"phone_number = '010\\' 3167 1897' ")(true)
}
}
test("check where clause nested") {
for {
(srcVertex, tgtVertex, label, dir) <- ids
} {
/** test for each version */
val js = Json.obj("is_hidden" -> true, "is_blocked" -> false, "weight" -> 10, "time" -> 3, "name" -> "abc")
val propsInner = Management.toProps(label, js.fields).map { case (k, v) => k -> InnerValLikeWithTs(v, ts) }.toMap + dummyTs
val edge = builder.newEdge(srcVertex, tgtVertex, label, labelWithDir.dir, 0.toByte, ts, propsInner)
val f = validate(label)(edge) _
// time == 3
f("time >= 3")(true)
f("time > 2")(true)
f("time <= 3")(true)
f("time < 2")(false)
f("NOT time >= 3")(false)
f("(time in (1, 2, 3) and is_blocked = true) or is_hidden = false")(false)
f("(time in (1, 2, 3) or is_blocked = true) or is_hidden = false")(true)
f("(time in (1, 2, 3) and is_blocked = true) or is_hidden = true")(true)
f("(time in (1, 2, 3) or is_blocked = true) and is_hidden = true")(true)
f("((time in ( 1, 2, 3) and weight between 1 and 10) or is_hidden=false)")(true)
f("(time in (1, 2, 4 ) or weight between 1 and 9) or (is_hidden = false)")(false)
f("(time in ( 1,2,4 ) or weight between 1 and 9) or is_hidden= true")(true)
f("(time in (1,2,3) or weight between 1 and 10) and is_hidden =false")(false)
}
}
test("check where clause with from/to long") {
for {
(srcVertex, tgtVertex, label, dir) <- ids
} {
/** test for each version */
val js = Json.obj("is_hidden" -> true, "is_blocked" -> false, "weight" -> 10, "time" -> 3, "name" -> "abc")
val propsInner = Management.toProps(label, js.fields).map { case (k, v) => k -> InnerValLikeWithTs(v, ts) }.toMap + dummyTs
val edge = builder.newEdge(srcVertex, tgtVertex, label, dir, 0.toByte, ts, propsInner)
val f = validate(label)(edge) _
f(s"_from = -1 or _to = ${tgtVertex.innerId.value}")(true)
f(s"_to = 2")(true)
f(s"_from = ${srcVertex.innerId.value} and _to = ${tgtVertex.innerId.value}")(true)
f(s"_from = ${tgtVertex.innerId.value} and _to = 102934")(false)
f(s"_from = -1")(false)
f(s"_from in (-1, -0.1)")(false)
f(s"NOT (_from = -1)")(true)
f(s"NOT _from contains 'a'")(true)
}
}
test("check where clause with parent") {
for {
(srcVertex, tgtVertex, label, dir) <- ids
} {
/** test for each version */
val js = Json.obj("is_hidden" -> true, "is_blocked" -> false, "weight" -> 10, "time" -> 1, "name" -> "abc")
val parentJs = Json.obj("is_hidden" -> false, "is_blocked" -> false, "weight" -> 20, "time" -> 3, "name" -> "a")
val propsInner = Management.toProps(label, js.fields).map { case (k, v) => k -> InnerValLikeWithTs(v, ts) }.toMap + dummyTs
val parentPropsInner = Management.toProps(label, parentJs.fields).map { case (k, v) => k -> InnerValLikeWithTs(v, ts) }.toMap + dummyTs
val grandParentEdge = builder.newEdge(srcVertex, tgtVertex, label, labelWithDir.dir, 0.toByte, ts, parentPropsInner)
val parentEdge = builder.newEdge(srcVertex, tgtVertex, label, labelWithDir.dir, 0.toByte, ts, parentPropsInner,
parentEdges = Seq(EdgeWithScore(grandParentEdge, 1.0, grandParentEdge.innerLabel)))
val edge = builder.newEdge(srcVertex, tgtVertex, label, labelWithDir.dir, 0.toByte, ts, propsInner,
parentEdges = Seq(EdgeWithScore(parentEdge, 1.0, grandParentEdge.innerLabel)))
println(edge.toString)
println(parentEdge.toString)
println(grandParentEdge.toString)
val f = validate(label)(edge) _
// Compare edge's prop(`_from`) with edge's prop(`name`)
f("_from = 1")(true)
f("_to = 2")(true)
f("_from = 123")(false)
f("_from = time")(true)
// Compare edge's prop(`weight`) with edge's prop(`time`)
f("weight = time")(false)
f("weight = is_blocked")(false)
// Compare edge's prop(`weight`) with parent edge's prop(`weight`)
f("_parent.is_blocked = is_blocked")(true)
f("is_hidden = _parent.is_hidden")(false)
f("_parent.weight = weight")(false)
// Compare edge's prop(`is_hidden`) with parent of parent edge's prop(`is_hidden`)
f("_parent._parent.is_hidden = is_hidden")(false)
f("_parent._parent.is_blocked = is_blocked")(true)
f("_parent._parent.weight = weight")(false)
f("_parent._parent.weight = _parent.weight")(true)
}
}
test("replace reserved") {
val ts = 0
import TemplateHelper._
calculate(ts, 1, "minute") should be(minute + ts)
calculate(ts, 1, "hour") should be(hour + ts)
calculate(ts, 1, "day") should be(day + ts)
calculate(ts + 10, 1, "HOUR") should be(hour + ts + 10)
calculate(ts + 10, 1, "DAY") should be(day + ts + 10)
val body =
"""{
"minute": ${1 minute},
"day": ${1day},
"hour": ${1hour},
"-day": "${-10 day}",
"-hour": ${-10 hour},
"now": "${now}"
}
"""
val parsed = replaceVariable(ts, body)
val json = Json.parse(parsed)
(json \ "minute").as[Long] should be(1 * minute + ts)
(json \ "day").as[Long] should be(1 * day + ts)
(json \ "hour").as[Long] should be(1 * hour + ts)
(json \ "-day").as[Long] should be(-10 * day + ts)
(json \ "-hour").as[Long] should be(-10 * hour + ts)
(json \ "now").as[Long] should be(ts)
val otherBody =
"""{
"nextminute": "${next_minute}",
"nextday": "${next_day}",
"3dayago": "${next_day - 3 day}",
"nexthour": "${next_hour}"
}"""
val currentTs = 1474422964000l
val expectedMinuteTs = currentTs / minute * minute + minute
val expectedDayTs = currentTs / day * day + day
val expectedHourTs = currentTs / hour * hour + hour
val threeDayAgo = expectedDayTs - 3 * day
val currentTsLs = (1 until 1000).map(currentTs + _)
currentTsLs.foreach { ts =>
val parsed = replaceVariable(ts, otherBody)
val json = Json.parse(parsed)
(json \ "nextminute").as[Long] should be(expectedMinuteTs)
(json \ "nextday").as[Long] should be(expectedDayTs)
(json \ "nexthour").as[Long] should be(expectedHourTs)
(json \ "3dayago").as[Long] should be(threeDayAgo)
}
(0 until 1000).forall { ith =>
val r = replaceVariable(ts, "${randint( 10, 30 )}").toInt
r >= 10 && r < 30
}
}
test("check parse contains") {
val dummyLabel = ids.head._3
val sql = "name = 'daewon'"
val whereOpt = WhereParser().parse(sql)
println(whereOpt)
}
}