Repository: incubator-s2graph
Updated Branches:
  refs/heads/master 7951f5520 -> 146094b50


[S2GRAPH-135] Change the way LabelIndexOption is implemented and improve it


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

Branch: refs/heads/master
Commit: 20bdf9291acfbb830db441490e1d5351fa8a0ca3
Parents: 247b2cb
Author: daewon <[email protected]>
Authored: Fri Dec 2 00:11:04 2016 +0900
Committer: daewon <[email protected]>
Committed: Fri Dec 2 00:24:26 2016 +0900

----------------------------------------------------------------------
 .../scala/org/apache/s2graph/core/S2Edge.scala  |  52 ++++---
 .../apache/s2graph/core/mysqls/LabelIndex.scala |  52 +++----
 .../apache/s2graph/core/storage/Storage.scala   |  44 ++----
 .../core/Integrate/IntegrateCommon.scala        |  63 +++++++-
 .../core/Integrate/LabelIndexOptionTest.scala   | 144 +++++++++++++++++++
 5 files changed, 272 insertions(+), 83 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/20bdf929/s2core/src/main/scala/org/apache/s2graph/core/S2Edge.scala
----------------------------------------------------------------------
diff --git a/s2core/src/main/scala/org/apache/s2graph/core/S2Edge.scala 
b/s2core/src/main/scala/org/apache/s2graph/core/S2Edge.scala
index 2960265..5c2a5dc 100644
--- a/s2core/src/main/scala/org/apache/s2graph/core/S2Edge.scala
+++ b/s2core/src/main/scala/org/apache/s2graph/core/S2Edge.scala
@@ -136,6 +136,8 @@ case class IndexEdge(graph: S2Graph,
 
   lazy val labelIndexMetaSeqs = labelIndex.sortKeyTypes
 
+  def indexOption = if (isInEdge) labelIndex.inDirOption else 
labelIndex.outDirOption
+
   /** TODO: make sure call of this class fill props as this assumes */
   lazy val orders = for (meta <- labelIndexMetaSeqs) yield {
     propsWithTs.get(meta.name) match {
@@ -149,8 +151,10 @@ case class IndexEdge(graph: S2Graph,
           case LabelMeta.timestamp=> InnerVal.withLong(version, schemaVer)
           case LabelMeta.to => toEdge.tgtVertex.innerId
           case LabelMeta.from => toEdge.srcVertex.innerId
+          case LabelMeta.fromHash => indexOption.map { option =>
+            
InnerVal.withLong(MurmurHash3.stringHash(toEdge.srcVertex.innerId.toString()).abs
 % option.totalModular, schemaVer)
+          }.getOrElse(throw new RuntimeException("from_hash must be used with 
sampling"))
           // for now, it does not make sense to build index on 
srcVertex.innerId since all edges have same data.
-          //            throw new RuntimeException("_from on indexProps is not 
supported")
           case _ => toInnerVal(meta.defaultValue, meta.dataType, schemaVer)
         }
 
@@ -574,10 +578,34 @@ case class S2Edge(innerGraph: S2Graph,
 }
 
 
+object EdgeMutate {
+  def filterIndexOptionForDegree(edges: Seq[IndexEdge]): Seq[IndexEdge] = 
edges.filter { ie =>
+    ie.indexOption.fold(true)(_.storeDegree)
+  }
+
+  def filterIndexOption(edges: Seq[IndexEdge]): Seq[IndexEdge] = edges.filter 
{ ie =>
+    ie.indexOption.fold(true) { option =>
+      val hashValueOpt = ie.orders.find { case (k, v) => k == 
LabelMeta.fromHash }.map { case (k, v) =>
+        v.value.toString.toLong
+      }
+
+      option.sample(ie, hashValueOpt)
+    }
+  }
+}
+
 case class EdgeMutate(edgesToDelete: List[IndexEdge] = List.empty[IndexEdge],
                       edgesToInsert: List[IndexEdge] = List.empty[IndexEdge],
                       newSnapshotEdge: Option[SnapshotEdge] = None) {
 
+  val edgesToInsertWithIndexOpt: Seq[IndexEdge] = 
EdgeMutate.filterIndexOption(edgesToInsert)
+
+  val edgesToDeleteWithIndexOpt: Seq[IndexEdge] = 
EdgeMutate.filterIndexOption(edgesToDelete)
+
+  val edgesToInsertWithIndexOptForDegree: Seq[IndexEdge] = 
EdgeMutate.filterIndexOptionForDegree(edgesToInsert)
+
+  val edgesToDeleteWithIndexOptForDegree: Seq[IndexEdge] = 
EdgeMutate.filterIndexOptionForDegree(edgesToDelete)
+
   def toLogString: String = {
     val l = (0 until 50).map(_ => "-").mkString("")
     val deletes = s"deletes: ${edgesToDelete.map(e => 
e.toLogString).mkString("\n")}"
@@ -745,24 +773,6 @@ object S2Edge {
     }
   }
 
-  def filterOutWithLabelOption(ls: Seq[IndexEdge]): Seq[IndexEdge] = ls.filter 
{ ie =>
-    ie.labelIndex.dir match {
-      case None =>
-        // both direction use same indices that is defined when label creation.
-        true
-      case Some(dir) =>
-        if (dir != ie.dir) {
-          // current labelIndex's direction is different with indexEdge's 
direction so don't touch
-          false
-        } else {
-          ie.labelIndex.writeOption.map { option =>
-            val hashValueOpt = ie.orders.find { case (k, v) => k == 
LabelMeta.fromHash }.map{ case (k, v) => v.value.toString.toLong }
-            option.sample(ie, hashValueOpt)
-          }.getOrElse(true)
-        }
-    }
-  }
-
   def buildMutation(snapshotEdgeOpt: Option[S2Edge],
                     requestEdge: S2Edge,
                     newVersion: Long,
@@ -793,7 +803,7 @@ object S2Edge {
         val edgesToDelete = snapshotEdgeOpt match {
           case Some(snapshotEdge) if snapshotEdge.op != 
GraphUtil.operations("delete") =>
             snapshotEdge.copy(op = GraphUtil.defaultOpByte)
-              .relatedEdges.flatMap { relEdge => 
filterOutWithLabelOption(relEdge.edgesWithIndexValid) }
+              .relatedEdges.flatMap { relEdge => relEdge.edgesWithIndexValid }
           case _ => Nil
         }
 
@@ -807,7 +817,7 @@ object S2Edge {
             )
             newPropsWithTs.foreach { case (k, v) => newEdge.property(k.name, 
v.innerVal.value, v.ts) }
 
-            newEdge.relatedEdges.flatMap { relEdge => 
filterOutWithLabelOption(relEdge.edgesWithIndexValid) }
+            newEdge.relatedEdges.flatMap { relEdge => 
relEdge.edgesWithIndexValid }
           }
 
 

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/20bdf929/s2core/src/main/scala/org/apache/s2graph/core/mysqls/LabelIndex.scala
----------------------------------------------------------------------
diff --git 
a/s2core/src/main/scala/org/apache/s2graph/core/mysqls/LabelIndex.scala 
b/s2core/src/main/scala/org/apache/s2graph/core/mysqls/LabelIndex.scala
index 7b1cd07..fa61149 100644
--- a/s2core/src/main/scala/org/apache/s2graph/core/mysqls/LabelIndex.scala
+++ b/s2core/src/main/scala/org/apache/s2graph/core/mysqls/LabelIndex.scala
@@ -19,10 +19,6 @@
 
 package org.apache.s2graph.core.mysqls
 
-/**
- * Created by shon on 6/3/15.
- */
-
 import org.apache.s2graph.core.GraphUtil
 import org.apache.s2graph.core.mysqls.LabelIndex.WriteOption
 import org.apache.s2graph.core.utils.logger
@@ -45,13 +41,14 @@ object LabelIndex extends Model[LabelIndex] {
       rs.stringOpt("options")
     )
   }
-  object WriteOption {
-    val Default = WriteOption()
-  }
-  case class WriteOption(method: String = "default",
-                         rate: Double = 1.0,
-                         totalModular: Long = 100,
-                         storeDegree: Boolean = true) {
+
+  case class WriteOption(dir: Byte,
+                         method: String,
+                         rate: Double,
+                         totalModular: Long,
+                         storeDegree: Boolean) {
+
+    val isBufferIncrement = method == "drop" || method == "sample" || method 
== "hash_sample"
 
     def sample[T](a: T, hashOpt: Option[Long]): Boolean = {
       if (method == "drop") false
@@ -182,29 +179,36 @@ case class LabelIndex(id: Option[Int], labelId: Int, 
name: String, seq: Byte, me
   lazy val sortKeyTypesArray = sortKeyTypes.toArray
   lazy val propNames = sortKeyTypes.map { labelMeta => labelMeta.name }
 
-  val dirJs = dir.map(GraphUtil.fromDirection).getOrElse("both")
-  val optionsJs = try { options.map(Json.parse).getOrElse(Json.obj()) } catch 
{ case e: Exception => Json.obj() }
-  lazy val toJson = Json.obj(
-    "name" -> name,
-    "propNames" -> sortKeyTypes.map(x => x.name),
-    "dir" -> dirJs,
-    "options" -> optionsJs
-  )
+  lazy val toJson = {
+    val dirJs = dir.map(GraphUtil.fromDirection).getOrElse("both")
+    val optionsJs = try { options.map(Json.parse).getOrElse(Json.obj()) } 
catch { case e: Exception => Json.obj() }
 
-  lazy val writeOption: Option[WriteOption] = try {
+    Json.obj(
+      "name" -> name,
+      "propNames" -> sortKeyTypes.map(x => x.name),
+      "dir" -> dirJs,
+      "options" -> optionsJs
+    )
+  }
+
+  def parseOption(dir: String): Option[WriteOption] = try {
     options.map { string =>
-      val jsObj = Json.parse(string)
-      val method = (jsObj \ "method").as[String]
+      val jsObj = Json.parse(string) \ dir
 
+      val method = (jsObj \ "method").asOpt[String].getOrElse("default")
       val rate = (jsObj \ "rate").asOpt[Double].getOrElse(1.0)
       val totalModular = (jsObj \ "totalModular").asOpt[Long].getOrElse(100L)
-      val storeDegree = (jsObj \ "degree").asOpt[Boolean].getOrElse(true)
+      val storeDegree = (jsObj \ "storeDegree").asOpt[Boolean].getOrElse(true)
 
-      WriteOption(method, rate, totalModular, storeDegree)
+      WriteOption(GraphUtil.directions(dir).toByte, method, rate, 
totalModular, storeDegree)
     }
   } catch {
     case e: Exception =>
       logger.error(s"Parse failed labelOption: ${this.label}", e)
       None
   }
+
+  lazy val inDirOption = parseOption("in")
+
+  lazy val outDirOption = parseOption("out")
 }

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/20bdf929/s2core/src/main/scala/org/apache/s2graph/core/storage/Storage.scala
----------------------------------------------------------------------
diff --git 
a/s2core/src/main/scala/org/apache/s2graph/core/storage/Storage.scala 
b/s2core/src/main/scala/org/apache/s2graph/core/storage/Storage.scala
index 59b7518..421bec3 100644
--- a/s2core/src/main/scala/org/apache/s2graph/core/storage/Storage.scala
+++ b/s2core/src/main/scala/org/apache/s2graph/core/storage/Storage.scala
@@ -1023,16 +1023,12 @@ abstract class Storage[Q, R](val graph: S2Graph,
   /** EdgeMutate */
   def indexedEdgeMutations(edgeMutate: EdgeMutate): Seq[SKeyValue] = {
     // skip sampling for delete operation
-    val deleteMutations = edgeMutate.edgesToDelete.flatMap { indexEdge =>
+    val deleteMutations = edgeMutate.edgesToDeleteWithIndexOpt.flatMap { 
indexEdge =>
       indexEdgeSerializer(indexEdge).toKeyValues.map(_.copy(operation = 
SKeyValue.Delete, durability = indexEdge.label.durability))
     }
 
-    val insertMutations = edgeMutate.edgesToInsert.flatMap { indexEdge =>
-      if (indexEdge.isOutEdge) 
indexEdgeSerializer(indexEdge).toKeyValues.map(_.copy(operation = 
SKeyValue.Put, durability = indexEdge.label.durability))
-      else {
-        // For InEdge
-        indexEdgeSerializer(indexEdge).toKeyValues.map(_.copy(operation = 
SKeyValue.Put, durability = indexEdge.label.durability))
-      }
+    val insertMutations = edgeMutate.edgesToInsertWithIndexOpt.flatMap { 
indexEdge =>
+      indexEdgeSerializer(indexEdge).toKeyValues.map(_.copy(operation = 
SKeyValue.Put, durability = indexEdge.label.durability))
     }
 
     deleteMutations ++ insertMutations
@@ -1041,46 +1037,26 @@ abstract class Storage[Q, R](val graph: S2Graph,
   def snapshotEdgeMutations(edgeMutate: EdgeMutate): Seq[SKeyValue] =
     edgeMutate.newSnapshotEdge.map(e => 
snapshotEdgeSerializer(e).toKeyValues.map(_.copy(durability = 
e.label.durability))).getOrElse(Nil)
 
-  def incrementsInOut(edgeMutate: EdgeMutate): (Seq[SKeyValue], 
Seq[SKeyValue]) = {
-
-    def filterOutDegree(e: IndexEdge): Boolean =
-      e.labelIndex.writeOption.fold(true)(_.storeDegree)
-      
-    (edgeMutate.edgesToDelete.isEmpty, edgeMutate.edgesToInsert.isEmpty) match 
{
+  def increments(edgeMutate: EdgeMutate): Seq[SKeyValue] = {
+    (edgeMutate.edgesToDeleteWithIndexOptForDegree.isEmpty, 
edgeMutate.edgesToInsertWithIndexOptForDegree.isEmpty) match {
       case (true, true) =>
-
         /** when there is no need to update. shouldUpdate == false */
-        (Nil, Nil)
-      case (true, false) =>
+        Nil
 
+      case (true, false) =>
         /** no edges to delete but there is new edges to insert so increase 
degree by 1 */
-        val (inEdges, outEdges) = 
edgeMutate.edgesToInsert.partition(_.isInEdge)
+        
edgeMutate.edgesToInsertWithIndexOptForDegree.flatMap(buildIncrementsAsync(_))
 
-        val in = 
inEdges.filter(filterOutDegree).flatMap(buildIncrementsAsync(_))
-        val out = 
outEdges.filter(filterOutDegree).flatMap(buildIncrementsAsync(_))
-
-        in -> out
       case (false, true) =>
-
         /** no edges to insert but there is old edges to delete so decrease 
degree by 1 */
-        val (inEdges, outEdges) = 
edgeMutate.edgesToDelete.partition(_.isInEdge)
+        
edgeMutate.edgesToDeleteWithIndexOptForDegree.flatMap(buildIncrementsAsync(_, 
-1))
 
-        val in = 
inEdges.filter(filterOutDegree).flatMap(buildIncrementsAsync(_, -1))
-        val out = 
outEdges.filter(filterOutDegree).flatMap(buildIncrementsAsync(_, -1))
-
-        in -> out
       case (false, false) =>
-
         /** update on existing edges so no change on degree */
-        (Nil, Nil)
+        Nil
     }
   }
 
-  def increments(edgeMutate: EdgeMutate): Seq[SKeyValue] = {
-    val (in, out) = incrementsInOut(edgeMutate)
-    in ++ out
-  }
-
   /** IndexEdge */
   def buildIncrementsAsync(indexedEdge: IndexEdge, amount: Long = 1L): 
Seq[SKeyValue] = {
     val newProps = indexedEdge.updatePropsWithTs()

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/20bdf929/s2core/src/test/scala/org/apache/s2graph/core/Integrate/IntegrateCommon.scala
----------------------------------------------------------------------
diff --git 
a/s2core/src/test/scala/org/apache/s2graph/core/Integrate/IntegrateCommon.scala 
b/s2core/src/test/scala/org/apache/s2graph/core/Integrate/IntegrateCommon.scala
index d280570..b4d6af6 100644
--- 
a/s2core/src/test/scala/org/apache/s2graph/core/Integrate/IntegrateCommon.scala
+++ 
b/s2core/src/test/scala/org/apache/s2graph/core/Integrate/IntegrateCommon.scala
@@ -6,9 +6,9 @@
  * 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
@@ -70,7 +70,8 @@ trait IntegrateCommon extends FunSuite with Matchers with 
BeforeAndAfterAll {
     val labelNames = Map(testLabelName -> testLabelNameCreate,
       testLabelName2 -> testLabelName2Create,
       testLabelNameV1 -> testLabelNameV1Create,
-      testLabelNameWeak -> testLabelNameWeakCreate)
+      testLabelNameWeak -> testLabelNameWeakCreate,
+      testLabelNameLabelIndex -> testLabelNameLabelIndexCreate)
 
     for {
       (labelName, create) <- labelNames
@@ -135,7 +136,7 @@ trait IntegrateCommon extends FunSuite with Matchers with 
BeforeAndAfterAll {
       logger.info(s2Query.toString)
       val stepResult = Await.result(graph.getEdges(s2Query), 
HttpRequestWaitingTime)
       val result = PostProcess.toJson(Option(s2Query.jsonQuery))(graph, 
s2Query.queryOption, stepResult)
-//      val result = 
Await.result(graph.getEdges(s2Query).(PostProcess.toJson), 
HttpRequestWaitingTime)
+      //      val result = 
Await.result(graph.getEdges(s2Query).(PostProcess.toJson), 
HttpRequestWaitingTime)
       logger.debug(s"${Json.prettyPrint(result)}")
       result
     }
@@ -169,6 +170,7 @@ trait IntegrateCommon extends FunSuite with Matchers with 
BeforeAndAfterAll {
     val testLabelName2 = "s2graph_label_test_2"
     val testLabelNameV1 = "s2graph_label_test_v1"
     val testLabelNameWeak = "s2graph_label_test_weak"
+    val testLabelNameLabelIndex = "s2graph_label_test_index"
     val testColumnName = "user_id_test"
     val testColumnType = "long"
     val testTgtColumnName = "item_id_test"
@@ -176,6 +178,12 @@ trait IntegrateCommon extends FunSuite with Matchers with 
BeforeAndAfterAll {
     val newHTableName = "new-htable"
     val index1 = "idx_1"
     val index2 = "idx_2"
+    val idxStoreInDropDegree = "idx_drop_In"
+    val idxStoreOutDropDegree = "idx_drop_out"
+    val idxStoreIn = "idx_store_In"
+    val idxStoreOut = "idx_store_out"
+    val idxDropInStoreDegree = "idx_drop_in_store_degree"
+    val idxDropOutStoreDegree = "idx_drop_out_store_degree"
 
     val NumOfEachTest = 30
     val HttpRequestWaitingTime = Duration("60 seconds")
@@ -342,5 +350,52 @@ trait IntegrateCommon extends FunSuite with Matchers with 
BeforeAndAfterAll {
     "isDirected": true,
     "compressionAlgorithm": "gz"
   }"""
+
+    val testLabelNameLabelIndexCreate =
+      s"""
+  {
+    "label": "$testLabelNameLabelIndex",
+    "srcServiceName": "$testServiceName",
+    "srcColumnName": "$testColumnName",
+    "srcColumnType": "long",
+    "tgtServiceName": "$testServiceName",
+    "tgtColumnName": "$testColumnName",
+    "tgtColumnType": "long",
+    "indices": [
+       {"name": "$index1", "propNames": ["weight", "time", "is_hidden", 
"is_blocked"]},
+       {"name": "$idxStoreInDropDegree", "propNames": ["time"], "options": { 
"in": {"storeDegree": false }, "out": {"method": "drop", "storeDegree": false 
}}},
+       {"name": "$idxStoreOutDropDegree", "propNames": ["weight"], "options": 
{ "out": {"storeDegree": false}, "in": { "method": "drop", "storeDegree": false 
}}},
+       {"name": "$idxStoreIn", "propNames": ["is_hidden"], "options": { "out": 
{"method": "drop", "storeDegree": false }}},
+       {"name": "$idxStoreOut", "propNames": ["weight", "is_blocked"], 
"options": { "in": {"method": "drop", "storeDegree": false }, "out": {"method": 
"normal" }}},
+       {"name": "$idxDropInStoreDegree", "propNames": ["is_blocked"], 
"options": { "in": {"method": "drop" }, "out": {"method": "drop", 
"storeDegree": false }}},
+       {"name": "$idxDropOutStoreDegree", "propNames": ["weight", 
"is_blocked", "_timestamp"], "options": { "in": {"method": "drop", 
"storeDegree": false }, "out": {"method": "drop"}}}
+    ],
+    "props": [
+    {
+      "name": "time",
+      "dataType": "long",
+      "defaultValue": 0
+    },
+    {
+      "name": "weight",
+      "dataType": "long",
+      "defaultValue": 0
+    },
+    {
+      "name": "is_hidden",
+      "dataType": "boolean",
+      "defaultValue": false
+    },
+    {
+      "name": "is_blocked",
+      "dataType": "boolean",
+      "defaultValue": false
+    }
+    ],
+    "consistencyLevel": "strong",
+    "schemaVersion": "v4",
+    "compressionAlgorithm": "gz",
+    "hTableName": "$testHTableName"
+  }"""
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-s2graph/blob/20bdf929/s2core/src/test/scala/org/apache/s2graph/core/Integrate/LabelIndexOptionTest.scala
----------------------------------------------------------------------
diff --git 
a/s2core/src/test/scala/org/apache/s2graph/core/Integrate/LabelIndexOptionTest.scala
 
b/s2core/src/test/scala/org/apache/s2graph/core/Integrate/LabelIndexOptionTest.scala
new file mode 100644
index 0000000..6e213b0
--- /dev/null
+++ 
b/s2core/src/test/scala/org/apache/s2graph/core/Integrate/LabelIndexOptionTest.scala
@@ -0,0 +1,144 @@
+/*
+ * 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.Integrate
+
+import org.apache.s2graph.core._
+import org.scalatest.BeforeAndAfterEach
+import play.api.libs.json._
+
+class LabelIndexOptionTest extends IntegrateCommon with BeforeAndAfterEach {
+
+  import TestUtil._
+
+  // called by start test, once
+  override def initTestData(): Unit = {
+    super.initTestData()
+
+    val insert = "insert"
+    val e = "e"
+    val weight = "weight"
+    val is_hidden = "is_hidden"
+
+    insertEdgesSync(
+      toEdge(1, insert, e, 0, 1, testLabelNameLabelIndex),
+      toEdge(1, insert, e, 0, 2, testLabelNameLabelIndex),
+      toEdge(1, insert, e, 0, 3, testLabelNameLabelIndex)
+    )
+  }
+
+  def getQuery(ids: Seq[Int], direction: String, indexName: String): Query =
+    Query(
+      vertices = ids.map(graph.toVertex(testServiceName, testColumnName, _)),
+      steps = Vector(
+        Step(Seq(QueryParam(testLabelNameLabelIndex, direction = direction, 
indexName = indexName)))
+      )
+    )
+
+  /**
+    * "indices": [
+    * {"name": "$index1", "propNames": ["weight", "time", "is_hidden", 
"is_blocked"]},
+    * {"name": "$idxStoreInDropDegree", "propNames": ["time"], "options": { 
"in": {"storeDegree": false }, "out": {"method": "drop", "storeDegree": false 
}}},
+    * {"name": "$idxStoreOutDropDegree", "propNames": ["weight"], "options": { 
"out": {"storeDegree": false}, "in": { "method": "drop", "storeDegree": false 
}}},
+    * {"name": "$idxStoreIn", "propNames": ["is_hidden"], "options": { "out": 
{"method": "drop", "storeDegree": false }}},
+    * {"name": "$idxStoreOut", "propNames": ["weight", "is_blocked"], 
"options": { "in": {"method": "drop", "storeDegree": false }, "out": {"method": 
"normal" }}},
+    * {"name": "$idxDropInStoreDegree", "propNames": ["is_blocked"], 
"options": { "in": {"method": "drop" }, "out": {"method": "drop", 
"storeDegree": false }}},
+    * {"name": "$idxDropOutStoreDegree", "propNames": ["weight", "is_blocked", 
"_timestamp"], "options": { "in": {"method": "drop", "storeDegree": false }, 
"out": {"method": "drop"}}}
+    * ],
+    **/
+
+  /**
+    * index without no options
+    */
+  test("normal index should store in/out direction Edges with Degrees") {
+    var edges = getEdgesSync(getQuery(Seq(0), "out", index1))
+    (edges \ "results").as[Seq[JsValue]].size should be(3)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(3)
+
+    edges = getEdgesSync(getQuery(Seq(1, 2, 3), "in", index1))
+    (edges \ "results").as[Seq[JsValue]].size should be(3)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(3)
+  }
+
+  /**
+    * { "out": {"method": "drop", "storeDegree": false } }
+    */
+  test("storeDegree: store out direction Edge and drop Degree") {
+    val edges = getEdgesSync(getQuery(Seq(0), "out", idxStoreOutDropDegree))
+    (edges \ "results").as[Seq[JsValue]].size should be(3)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(0)
+  }
+
+  /**
+    * { "in": { "method": "drop", "storeDegree": false } }
+    */
+  test("storeDegree: store in direction Edge and drop Degree") {
+    val edges = getEdgesSync(getQuery(Seq(1, 2, 3), "in", 
idxStoreInDropDegree))
+    (edges \ "results").as[Seq[JsValue]].size should be(3)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(0)
+  }
+
+  /**
+    * { "out": {"method": "drop", "storeDegree": false } }
+    */
+  test("index for in direction should drop out direction edge") {
+    val edges = getEdgesSync(getQuery(Seq(0), "out", idxStoreIn))
+    (edges \ "results").as[Seq[JsValue]].size should be(0)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(0)
+  }
+
+  test("index for in direction should store in direction edge") {
+    val edges = getEdgesSync(getQuery(Seq(1, 2, 3), "in", idxStoreIn))
+    (edges \ "results").as[Seq[JsValue]].size should be(3)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(3)
+  }
+
+  /**
+    * { "in": {"method": "drop", "storeDegree": false } }
+    */
+  test("index for out direction should store out direction edge") {
+    val edges = getEdgesSync(getQuery(Seq(0), "out", idxStoreOut))
+    (edges \ "results").as[Seq[JsValue]].size should be(3)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(3)
+  }
+
+  test("index for out direction should drop in direction edge") {
+    val edges = getEdgesSync(getQuery(Seq(1, 2, 3), "in", idxStoreOut))
+    (edges \ "results").as[Seq[JsValue]].size should be(0)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(0)
+  }
+
+  /**
+    * { "out": {"method": "drop", "storeDegree": false} }
+    */
+  test("index for in direction should drop in direction edge and store 
degree") {
+    val edges = getEdgesSync(getQuery(Seq(1, 2, 3), "in", 
idxDropInStoreDegree))
+    (edges \ "results").as[Seq[JsValue]].size should be(0)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(3)
+  }
+
+  /**
+    * { "in": {"method": "drop", "storeDegree": false }, "out": {"method": 
"drop"} }
+    */
+  test("index for out direction should drop out direction edge and store 
degree") {
+    val edges = getEdgesSync(getQuery(Seq(0), "out", idxDropOutStoreDegree))
+    (edges \ "results").as[Seq[JsValue]].size should be(0)
+    (edges \\ "_degree").map(_.as[Long]).sum should be(3)
+  }
+}

Reply via email to