You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@s2graph.apache.org by st...@apache.org on 2018/06/18 07:04:22 UTC

[1/5] incubator-s2graph git commit: add Not logical operator on WhereParser.

Repository: incubator-s2graph
Updated Branches:
  refs/heads/master 1881df05c -> 2083d136e


add Not logical operator on WhereParser.


Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/f288680c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/f288680c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/f288680c

Branch: refs/heads/master
Commit: f288680c46c23ac90fba1741baf494c061921277
Parents: 32eb344
Author: DO YUNG YOON <st...@apache.org>
Authored: Fri Jun 15 11:21:17 2018 +0900
Committer: DO YUNG YOON <st...@apache.org>
Committed: Fri Jun 15 11:21:17 2018 +0900

----------------------------------------------------------------------
 .../apache/s2graph/core/parsers/WhereParser.scala   | 16 ++++++++--------
 .../s2graph/core/parsers/WhereParserTest.scala      |  4 ++++
 2 files changed, 12 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/f288680c/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
----------------------------------------------------------------------
diff --git a/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala b/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
index 75e9657..d6e176b 100644
--- a/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
+++ b/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
@@ -198,10 +198,14 @@ case class WhereParser() extends JavaTokenParsers {
 
   def paren: Parser[Clause] = "(" ~> clause <~ ")"
 
-  def clause: Parser[Clause] = (predicate | paren) * (and ^^^ { (a: Clause, b: Clause) => And(a, b) } | or ^^^ { (a: Clause, b: Clause) => Or(a, b) })
+  def clause: Parser[Clause] = (_not | predicate | paren) * (and ^^^ { (a: Clause, b: Clause) => And(a, b) } | or ^^^ { (a: Clause, b: Clause) => Or(a, b) })
 
   def identWithDot: Parser[String] = repsep(ident, ".") ^^ { case values => values.mkString(".") }
 
+  val _not = "not|NOT".r ~ (predicate | paren) ^^ {
+    case op ~ p => Not(p)
+  }
+
   val _eq = identWithDot ~ ("!=" | "=") ~ stringLiteral ^^ {
     case f ~ op ~ s => if (op == "=") Eq(f, s) else Not(Eq(f, s))
   }
@@ -219,14 +223,10 @@ case class WhereParser() extends JavaTokenParsers {
     case f ~ minV ~ maxV => Between(f, minV, maxV)
   }
 
-  val _in = identWithDot ~ (notIn | in) ~ ("(" ~> repsep(stringLiteral, ",") <~ ")") ^^ {
+  val _in = identWithDot ~ (in) ~ ("(" ~> repsep(stringLiteral, ",") <~ ")") ^^ {
     case f ~ op ~ values =>
-      val inClause =
-        if (f.startsWith("_parent")) IN(f, values.toSet)
-        else InWithoutParent(f, values.toSet)
-
-      if (op.toLowerCase == "in") inClause
-      else Not(inClause)
+      if (f.startsWith("_parent")) IN(f, values.toSet)
+      else InWithoutParent(f, values.toSet)
   }
 
   val _contains = identWithDot ~ contains ~ stringLiteral ^^ {

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/f288680c/s2core/src/test/scala/org/apache/s2graph/core/parsers/WhereParserTest.scala
----------------------------------------------------------------------
diff --git a/s2core/src/test/scala/org/apache/s2graph/core/parsers/WhereParserTest.scala b/s2core/src/test/scala/org/apache/s2graph/core/parsers/WhereParserTest.scala
index 342d9c6..0ff4db1 100644
--- a/s2core/src/test/scala/org/apache/s2graph/core/parsers/WhereParserTest.scala
+++ b/s2core/src/test/scala/org/apache/s2graph/core/parsers/WhereParserTest.scala
@@ -46,6 +46,7 @@ class WhereParserTest extends FunSuite with Matchers with TestCommonWithModels {
     }
 
     val whereOpt = WhereParser().parse(sql)
+
     if (whereOpt.isFailure) {
       debug(whereOpt)
       whereOpt.get // touch exception
@@ -138,6 +139,7 @@ class WhereParserTest extends FunSuite with Matchers with TestCommonWithModels {
       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)
@@ -169,6 +171,8 @@ class WhereParserTest extends FunSuite with Matchers with TestCommonWithModels {
       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)
     }
   }
 


[4/5] incubator-s2graph git commit: run apache-rat on S2GRAPH-219

Posted by st...@apache.org.
run apache-rat on S2GRAPH-219


Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/73624153
Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/73624153
Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/73624153

Branch: refs/heads/master
Commit: 73624153278edcee70769304a755956e22ac8bd8
Parents: 1881df0 a7ed4e8
Author: DO YUNG YOON <st...@apache.org>
Authored: Mon Jun 18 16:02:49 2018 +0900
Committer: DO YUNG YOON <st...@apache.org>
Committed: Mon Jun 18 16:02:49 2018 +0900

----------------------------------------------------------------------
 .../s2graph/core/parsers/WhereParser.scala       | 10 +++++++---
 .../s2graph/core/parsers/WhereParserTest.scala   |  4 ++++
 .../graphql/middleware/GraphFormatWriter.scala   | 19 +++++++++++++++++++
 .../graphql/types/PlayJsonScalarType.scala       | 19 +++++++++++++++++++
 4 files changed, 49 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/73624153/s2graphql/src/main/scala/org/apache/s2graph/graphql/middleware/GraphFormatWriter.scala
----------------------------------------------------------------------
diff --cc s2graphql/src/main/scala/org/apache/s2graph/graphql/middleware/GraphFormatWriter.scala
index 91fea62,0000000..6dcc368
mode 100644,000000..100644
--- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/middleware/GraphFormatWriter.scala
+++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/middleware/GraphFormatWriter.scala
@@@ -1,101 -1,0 +1,120 @@@
++/*
++ * 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.graphql.middleware
 +
 +import org.apache.s2graph.core.schema.ServiceColumn
 +import org.apache.s2graph.core.{GraphElement, S2EdgeLike, S2VertexLike}
 +import org.apache.s2graph.graphql.types.PlayJsonScalarType
 +import org.slf4s.LoggerFactory
 +import play.api.libs.json._
 +import sangria.execution._
 +import sangria.schema.Context
 +
 +
 +object GraphFormatted extends Middleware[Any] with MiddlewareAfterField[Any] with MiddlewareExtension[Any] {
 +  implicit val logger = LoggerFactory.getLogger(this.getClass)
 +
 +  type QueryVal = java.util.concurrent.ConcurrentHashMap[GraphElement, Unit]
 +
 +  type FieldVal = Long
 +
 +
 +  def beforeQuery(context: MiddlewareQueryContext[Any, _, _]) = {
 +    new java.util.concurrent.ConcurrentHashMap[GraphElement, Unit]()
 +  }
 +
 +  def afterQuery(queryVal: QueryVal, context: MiddlewareQueryContext[Any, _, _]) = ()
 +
 +  def toVertexId(v: S2VertexLike, c: ServiceColumn): String = {
 +    val innerId = v.innerId.toIdString()
 +
 +    s"${c.service.serviceName}.${c.columnName}.${innerId}"
 +  }
 +
 +  def toVertexJson(v: S2VertexLike, c: ServiceColumn): JsValue = {
 +    Json.obj(
 +      "id" -> toVertexId(v, c),
 +      "label" -> v.innerId.toIdString()
 +    )
 +  }
 +
 +  def toEdgeJson(e: S2EdgeLike): JsValue = {
 +    Json.obj(
 +      "source" -> toVertexId(e.srcVertex, e.innerLabel.srcColumn),
 +      "target" -> toVertexId(e.tgtVertex, e.innerLabel.tgtColumn),
 +      "id" -> s"${toVertexId(e.srcVertex, e.innerLabel.srcColumn)}.${e.label()}.${toVertexId(e.tgtVertex, e.innerLabel.tgtColumn)}",
 +      "label" -> e.label()
 +    )
 +  }
 +
 +  def afterQueryExtensions(queryVal: QueryVal,
 +                           context: MiddlewareQueryContext[Any, _, _]
 +                          ): Vector[Extension[_]] = {
 +
 +    import scala.collection.JavaConverters._
 +    val elements = queryVal.keys().asScala.toVector
 +
 +    val edges = elements.collect { case e: S2EdgeLike => e }
 +    val vertices = elements.collect { case v: S2VertexLike => v -> v.serviceColumn }
 +    val verticesFromEdges = edges.flatMap { e =>
 +      val label = e.innerLabel
 +      Vector((e.srcVertex, label.srcColumn), (e.tgtVertex, label.srcColumn))
 +    }
 +
 +    val verticesJson = (vertices ++ verticesFromEdges).map { case (v, c) => toVertexJson(v, c) }.distinct
 +    val edgeJson = edges.map(toEdgeJson).distinct
 +
 +    val jsElements = Json.obj(
 +      "nodes" -> verticesJson,
 +      "edges" -> edgeJson
 +    )
 +
 +    val graph = Json.obj("graph" -> jsElements)
 +
 +    /**
 +      * nodes: [{id, label, x, y, size}, ..],
 +      * edges: [{id, source, target, label}]
 +      */
 +    implicit val iu = PlayJsonScalarType.PlayJsonInputUnmarshaller
 +    Vector(Extension[JsValue](graph))
 +  }
 +
 +  def beforeField(queryVal: QueryVal, mctx: MiddlewareQueryContext[Any, _, _], ctx: Context[Any, _]) = {
 +    continue(System.currentTimeMillis())
 +  }
 +
 +  def afterField(queryVal: QueryVal, fieldVal: FieldVal, value: Any, mctx: MiddlewareQueryContext[Any, _, _], ctx: Context[Any, _]) = {
 +    //    logger.info(s"${ctx.parentType.name}.${ctx.field.name} = ${value.getClass.getName}")
 +
 +    value match {
 +      case ls: Seq[_] => ls.foreach {
 +        case e: GraphElement => queryVal.put(e, ())
 +        case _ =>
 +      }
 +      case e: GraphElement => queryVal.put(e, ())
 +      case _ =>
 +    }
 +
 +    None
 +  }
 +}
 +
 +
 +

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/73624153/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/PlayJsonScalarType.scala
----------------------------------------------------------------------
diff --cc s2graphql/src/main/scala/org/apache/s2graph/graphql/types/PlayJsonScalarType.scala
index 5149d36,0000000..fc7f1ce
mode 100644,000000..100644
--- a/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/PlayJsonScalarType.scala
+++ b/s2graphql/src/main/scala/org/apache/s2graph/graphql/types/PlayJsonScalarType.scala
@@@ -1,112 -1,0 +1,131 @@@
++/*
++ * 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.graphql.types
 +
 +import play.api.libs.json._
 +import sangria.ast
 +import sangria.execution.Executor
 +import sangria.marshalling.{ArrayMapBuilder, InputUnmarshaller, ResultMarshaller, ScalarValueInfo}
 +import sangria.schema._
 +import sangria.validation.{BigIntCoercionViolation, IntCoercionViolation, ValueCoercionViolation}
 +import sangria.macros._
 +
 +import scala.concurrent.ExecutionContext.Implicits.global
 +
 +object PlayJsonScalarType {
 +
 +  implicit object CustomPlayJsonResultMarshaller extends ResultMarshaller {
 +    type Node = JsValue
 +    type MapBuilder = ArrayMapBuilder[Node]
 +
 +    def emptyMapNode(keys: Seq[String]) = new ArrayMapBuilder[Node](keys)
 +
 +    def addMapNodeElem(builder: MapBuilder, key: String, value: Node, optional: Boolean) = builder.add(key, value)
 +
 +    def mapNode(builder: MapBuilder) = JsObject(builder.toMap)
 +
 +    def mapNode(keyValues: Seq[(String, JsValue)]) = Json.toJson(keyValues.toMap)
 +
 +    def arrayNode(values: Vector[JsValue]) = JsArray(values)
 +
 +    def optionalArrayNodeValue(value: Option[JsValue]) = value match {
 +      case Some(v) ⇒ v
 +      case None ⇒ nullNode
 +    }
 +
 +    def scalarNode(value: Any, typeName: String, info: Set[ScalarValueInfo]) = value match {
 +      case v: String ⇒ JsString(v)
 +      case v: Boolean ⇒ JsBoolean(v)
 +      case v: Int ⇒ JsNumber(v)
 +      case v: Long ⇒ JsNumber(v)
 +      case v: Float ⇒ JsNumber(BigDecimal(v))
 +      case v: Double ⇒ JsNumber(v)
 +      case v: BigInt ⇒ JsNumber(BigDecimal(v))
 +      case v: BigDecimal ⇒ JsNumber(v)
 +      case v: JsValue ⇒ v
 +      case v ⇒ throw new IllegalArgumentException("Unsupported scalar value: " + v)
 +    }
 +
 +    def enumNode(value: String, typeName: String) = JsString(value)
 +
 +    def nullNode = JsNull
 +
 +    def renderCompact(node: JsValue) = Json.stringify(node)
 +
 +    def renderPretty(node: JsValue) = Json.prettyPrint(node)
 +  }
 +
 +  implicit object PlayJsonInputUnmarshaller extends InputUnmarshaller[JsValue] {
 +    def getRootMapValue(node: JsValue, key: String) = node.asInstanceOf[JsObject].value get key
 +
 +    def isListNode(node: JsValue) = node.isInstanceOf[JsArray]
 +
 +    def getListValue(node: JsValue) = node.asInstanceOf[JsArray].value
 +
 +    def isMapNode(node: JsValue) = node.isInstanceOf[JsObject]
 +
 +    def getMapValue(node: JsValue, key: String) = node.asInstanceOf[JsObject].value get key
 +
 +    def getMapKeys(node: JsValue) = node.asInstanceOf[JsObject].fields.map(_._1)
 +
 +    def isDefined(node: JsValue) = node != JsNull
 +
 +    def getScalarValue(node: JsValue) = node match {
 +      case JsBoolean(b) ⇒ b
 +      case JsNumber(d) ⇒ d.toBigIntExact getOrElse d
 +      case JsString(s) ⇒ s
 +      case n ⇒ n
 +    }
 +
 +    def getScalaScalarValue(node: JsValue) = getScalarValue(node)
 +
 +    def isEnumNode(node: JsValue) = node.isInstanceOf[JsString]
 +
 +    def isScalarNode(node: JsValue) = true
 +
 +    def isVariableNode(node: JsValue) = false
 +
 +    def getVariableName(node: JsValue) = throw new IllegalArgumentException("variables are not supported")
 +
 +    def render(node: JsValue) = Json.stringify(node)
 +  }
 +
 +  case object JsonCoercionViolation extends ValueCoercionViolation("Not valid JSON")
 +
 +  implicit val JsonType = ScalarType[JsValue]("Json",
 +    description = Some("Raw PlayJson value"),
 +    coerceOutput = (value, _) ⇒ value,
 +    coerceUserInput = {
 +      case v: String ⇒ Right(JsString(v))
 +      case v: Boolean ⇒ Right(JsBoolean(v))
 +      case v: Int ⇒ Right(JsNumber(v))
 +      case v: Long ⇒ Right(JsNumber(v))
 +      case v: Float ⇒ Right(JsNumber(BigDecimal(v)))
 +      case v: Double ⇒ Right(JsNumber(v))
 +      case v: BigInt ⇒ Right(JsNumber(BigDecimal(v)))
 +      case v: BigDecimal ⇒ Right(JsNumber(v))
 +      case v: JsValue ⇒ Right(v)
 +    },
 +    coerceInput = {
 +      case sv: ast.StringValue => Right(Json.parse(sv.value))
 +      case _ ⇒
 +        Left(JsonCoercionViolation)
 +    })
 +}


[5/5] incubator-s2graph git commit: [S2GRAPH-222]: Support Not logical operator in WhereParser.

Posted by st...@apache.org.
[S2GRAPH-222]: Support Not logical operator in WhereParser.

JIRA:
  [S2GRAPH-222] https://issues.apache.org/jira/browse/S2GRAPH-222

Pull Request:
  Closes #174

Author
  DO YUNG YOON <st...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/2083d136
Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/2083d136
Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/2083d136

Branch: refs/heads/master
Commit: 2083d136e058cbfdd762c11c3292418a75b5d2a4
Parents: 7362415
Author: DO YUNG YOON <st...@apache.org>
Authored: Mon Jun 18 16:04:03 2018 +0900
Committer: DO YUNG YOON <st...@apache.org>
Committed: Mon Jun 18 16:04:03 2018 +0900

----------------------------------------------------------------------
 CHANGES | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/2083d136/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index a68ecef..b6bc0cd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -95,6 +95,7 @@ Release Notes - S2Graph - Version 0.2.0
     * [S2GRAPH-206] - Generalize machine learning model serving.
     * [S2GRAPH-215] - Implement a Storage Backend for JDBC driver, such as H2, MySql using the Mutator and Fetcher interfaces.
     * [S2GRAPH-219] - Added query that includes all vertices and associated edges for GraphVisualize.
+    * [S2GRAPH-222] - Support Not logical operator in WhereParser.
 
 ** Task
     * [S2GRAPH-162] - Update year in the NOTICE file.


[2/5] incubator-s2graph git commit: remove not in.

Posted by st...@apache.org.
remove not in.


Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/428c4535
Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/428c4535
Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/428c4535

Branch: refs/heads/master
Commit: 428c453506d3f89d22135f2518dc2a601f5070f8
Parents: f288680
Author: DO YUNG YOON <st...@apache.org>
Authored: Fri Jun 15 11:45:55 2018 +0900
Committer: DO YUNG YOON <st...@apache.org>
Committed: Fri Jun 15 11:45:55 2018 +0900

----------------------------------------------------------------------
 .../main/scala/org/apache/s2graph/core/parsers/WhereParser.scala | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/428c4535/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
----------------------------------------------------------------------
diff --git a/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala b/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
index d6e176b..a212dbc 100644
--- a/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
+++ b/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
@@ -192,8 +192,6 @@ case class WhereParser() extends JavaTokenParsers {
 
   val contains = "contains|CONTAINS".r
 
-  val notIn = "not in|NOT IN".r
-
   def where: Parser[Where] = rep(clause) ^^ (Where(_))
 
   def paren: Parser[Clause] = "(" ~> clause <~ ")"
@@ -223,7 +221,7 @@ case class WhereParser() extends JavaTokenParsers {
     case f ~ minV ~ maxV => Between(f, minV, maxV)
   }
 
-  val _in = identWithDot ~ (in) ~ ("(" ~> repsep(stringLiteral, ",") <~ ")") ^^ {
+  val _in = identWithDot ~ in ~ ("(" ~> repsep(stringLiteral, ",") <~ ")") ^^ {
     case f ~ op ~ values =>
       if (f.startsWith("_parent")) IN(f, values.toSet)
       else InWithoutParent(f, values.toSet)


[3/5] incubator-s2graph git commit: revert removing 'not in' clause.

Posted by st...@apache.org.
revert removing 'not in' clause.


Project: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/commit/a7ed4e8c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/tree/a7ed4e8c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-s2graph/diff/a7ed4e8c

Branch: refs/heads/master
Commit: a7ed4e8c56d6ba9f629466f4c2128834881f7f6a
Parents: 428c453
Author: DO YUNG YOON <st...@apache.org>
Authored: Mon Jun 18 12:38:03 2018 +0900
Committer: DO YUNG YOON <st...@apache.org>
Committed: Mon Jun 18 12:38:03 2018 +0900

----------------------------------------------------------------------
 .../org/apache/s2graph/core/parsers/WhereParser.scala | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/a7ed4e8c/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
----------------------------------------------------------------------
diff --git a/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala b/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
index a212dbc..4b9725e 100644
--- a/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
+++ b/s2core/src/main/scala/org/apache/s2graph/core/parsers/WhereParser.scala
@@ -190,8 +190,10 @@ case class WhereParser() extends JavaTokenParsers {
 
   val in = "in|IN".r
 
-  val contains = "contains|CONTAINS".r
+  val notIn = "not in|NOT IN".r
 
+  val contains = "contains|CONTAINS".r
+  
   def where: Parser[Where] = rep(clause) ^^ (Where(_))
 
   def paren: Parser[Clause] = "(" ~> clause <~ ")"
@@ -221,10 +223,14 @@ case class WhereParser() extends JavaTokenParsers {
     case f ~ minV ~ maxV => Between(f, minV, maxV)
   }
 
-  val _in = identWithDot ~ in ~ ("(" ~> repsep(stringLiteral, ",") <~ ")") ^^ {
+  val _in = identWithDot ~ (notIn | in) ~ ("(" ~> repsep(stringLiteral, ",") <~ ")") ^^ {
     case f ~ op ~ values =>
-      if (f.startsWith("_parent")) IN(f, values.toSet)
-      else InWithoutParent(f, values.toSet)
+      val inClause =
+        if (f.startsWith("_parent")) IN(f, values.toSet)
+        else InWithoutParent(f, values.toSet)
+
+      if (op.toLowerCase == "in") inClause
+      else Not(inClause)
   }
 
   val _contains = identWithDot ~ contains ~ stringLiteral ^^ {