From ae90ad237703ba875d8627c92e521ab3a8a4cf0b Mon Sep 17 00:00:00 2001 From: contrueCT Date: Sun, 27 Sep 2026 15:55:05 +0800 Subject: [PATCH 1/4] fix(core): reject incomplete source index coverage --- .../backend/tx/GraphIndexTransaction.java | 57 ++- .../apache/hugegraph/core/CoreTestSuite.java | 1 + .../apache/hugegraph/core/EdgeCoreTest.java | 260 ++++++------ .../core/SourceIndexCoverageTest.java | 362 ++++++++++++++++ .../apache/hugegraph/core/VertexCoreTest.java | 401 +++++++++--------- 5 files changed, 755 insertions(+), 326 deletions(-) create mode 100644 hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java index 6faace9671..d84917470a 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java @@ -480,7 +480,7 @@ private IdHolderList queryByUserprop(ConditionQuery query) { } } } - Set indexes = this.collectMatchedIndexes(query); + Set indexes = this.collectMatchedIndexes(query, true); if (indexes.isEmpty()) { Id label = query.condition(HugeKeys.LABEL); throw noIndexException(this.graph(), query, label); @@ -492,8 +492,8 @@ private IdHolderList queryByUserprop(ConditionQuery query) { return IdHolderList.empty(paging); } - // Do index query - IdHolderList holders = new IdHolderList(paging); + // Validate the whole plan before opening any backend iterators. A later + // label's unavailable index must not leave earlier iterators unowned. for (MatchedIndex index : indexes) { for (IndexLabel il : index.indexLabels()) { validateIndexLabel(il); @@ -501,7 +501,11 @@ private IdHolderList queryByUserprop(ConditionQuery query) { if (paging && index.indexLabels().size() > 1) { throw new NotSupportException("joint index query in paging"); } + } + // Do index query + IdHolderList holders = new IdHolderList(paging); + for (MatchedIndex index : indexes) { if (index.containsSearchIndex()) { // Do search-index query holders.addAll(this.doSearchIndex(query, index)); @@ -765,8 +769,13 @@ private PageIds doIndexQueryOnce(IndexLabel indexLabel, } } - @Watched(prefix = "index") private Set collectMatchedIndexes(ConditionQuery query) { + return this.collectMatchedIndexes(query, false); + } + + @Watched(prefix = "index") + private Set collectMatchedIndexes(ConditionQuery query, + boolean requireCompleteCoverage) { ISchemaTransaction schema = this.params().schemaTransaction(); Id label = query.condition(HugeKeys.LABEL); @@ -805,9 +814,49 @@ private Set collectMatchedIndexes(ConditionQuery query) { matchedIndexes.add(index); } } + if (requireCompleteCoverage && !matchedIndexes.isEmpty()) { + this.checkIndexCoverage(query, schemaLabels, matchedIndexes); + } return matchedIndexes; } + private void checkIndexCoverage(ConditionQuery query, + List labels, + Set indexes) { + // Check at execution, before creating any lazy index iterator. Cleanup + // callers only need matching indexes and must not require full coverage. + Set requiredProperties = query.userpropKeys(); + if (this.graph().readMode().showOlap()) { + // OLAP properties are shared rather than declared on every label. + requiredProperties.removeIf(id -> this.graph().propertyKey(id).olap()); + } + Set covered = new HashSet<>(); + for (MatchedIndex index : indexes) { + covered.add(index.schemaLabel().id()); + } + for (SchemaLabel label : labels) { + if (covered.contains(label.id()) || + !label.properties().containsAll(requiredProperties)) { + continue; + } + if (label instanceof EdgeLabel) { + EdgeLabel edgeLabel = (EdgeLabel) label; + // Base labels have no direct edges; their property indexes + // include edges of their sub-labels (see updateEdgeIndex()). + if (edgeLabel.isFather() || + edgeLabel.hasFather() && covered.contains(edgeLabel.fatherId())) { + continue; + } + } + // Authorization filters elements after the backend query. Do not + // disclose an implicitly discovered schema label in this error. + throw new NoIndexException("Incomplete index coverage for properties %s; " + + "specify a label or create compatible indexes " + + "for every candidate label", + this.graph().mapPkId2Name(query.userpropKeys())); + } + } + /** * Collect matched IndexLabel(s) in a SchemaLabel for a query * diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java index f18d656c08..1a4e64fe40 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java @@ -39,6 +39,7 @@ EdgeLabelCoreTest.class, IndexLabelCoreTest.class, VertexCoreTest.class, + SourceIndexCoverageTest.class, EdgeCoreTest.class, CountStrategyCoreTest.class, PrimaryKeyStrategyCoreTest.class, diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java index 9ea6b0e919..b9ba7674b7 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java @@ -755,7 +755,7 @@ public void testAddEdgeWithSecondaryIndexAndTtl() { "date", "2019-12-23 12:00:00"); graph().tx().commit(); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("place", "library of school"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge, edges.next()); @@ -767,7 +767,7 @@ public void testAddEdgeWithSecondaryIndexAndTtl() { // Ignore } - edges = graph().traversal().E().has("place", "library of school"); + edges = graph().traversal().E().hasLabel("read").has("place", "library of school"); Assert.assertFalse(edges.hasNext()); } @@ -797,27 +797,27 @@ public void testAddEdgeWithRangeIndexAndTtl() { "date", "2019-12-23 16:00:00"); graph().tx().commit(); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("date", "2019-12-23 14:00:00"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge3, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge5, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge1, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); @@ -829,22 +829,22 @@ public void testAddEdgeWithRangeIndexAndTtl() { // Ignore } - edges = graph().traversal().E().has("date", "2019-12-23 14:00:00"); + edges = graph().traversal().E().hasLabel("read").has("date", "2019-12-23 14:00:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); } @@ -878,31 +878,31 @@ public void testAddEdgeWithShardIndexAndTtl() { "date", "2019-12-23 14:00:00"); graph().tx().commit(); - Iterator edges = graph().traversal().E().has("place", "home"); + Iterator edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge6, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:00:00"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge3, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge5, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge1, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); @@ -914,26 +914,26 @@ public void testAddEdgeWithShardIndexAndTtl() { // Ignore } - edges = graph().traversal().E().has("place", "home"); + edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:00:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); } @@ -951,7 +951,7 @@ public void testAddEdgeWithSearchIndexAndTtl() { "date", "2019-12-23 12:00:00"); graph().tx().commit(); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("place", Text.contains("library")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge, edges.next()); @@ -963,7 +963,7 @@ public void testAddEdgeWithSearchIndexAndTtl() { // Ignore } - edges = graph().traversal().E().has("place", Text.contains("library")); + edges = graph().traversal().E().hasLabel("read").has("place", Text.contains("library")); Assert.assertFalse(edges.hasNext()); } @@ -1010,7 +1010,7 @@ public void testOverrideEdgeWithSecondaryIndexAndTtl() { "date", "2019-12-23 12:00:00"); graph().tx().commit(); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("place", "library of school"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge, edges.next()); @@ -1028,10 +1028,10 @@ public void testOverrideEdgeWithSecondaryIndexAndTtl() { graph().tx().commit(); // Due to overridden edges are expired, query will lead to async delete - edges = graph().traversal().E().has("place", "library of school"); + edges = graph().traversal().E().hasLabel("read").has("place", "library of school"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "home"); + edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge, edges.next()); graph().tx().commit(); @@ -1043,9 +1043,9 @@ public void testOverrideEdgeWithSecondaryIndexAndTtl() { } // All edges are expired after 6s - edges = graph().traversal().E().has("place", "library of school"); + edges = graph().traversal().E().hasLabel("read").has("place", "library of school"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "home"); + edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertFalse(edges.hasNext()); } @@ -1075,27 +1075,27 @@ public void testOverrideEdgeWithRangeIndexAndTtl() { "date", "2019-12-23 16:00:00"); graph().tx().commit(); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("date", "2019-12-23 14:00:00"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge3, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge5, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge1, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); @@ -1123,29 +1123,29 @@ public void testOverrideEdgeWithRangeIndexAndTtl() { "date", "2019-12-23 16:01:00"); graph().tx().commit(); // Due to overridden edges are expired, query will lead to async delete - edges = graph().traversal().E().has("date", "2019-12-23 14:00:00"); + edges = graph().traversal().E().hasLabel("read").has("date", "2019-12-23 14:00:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("date", "2019-12-23 14:01:00"); + edges = graph().traversal().E().hasLabel("read").has("date", "2019-12-23 14:01:00"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge8, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gt("2019-12-23 15:01:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge10, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gte("2019-12-23 15:01:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lt("2019-12-23 13:01:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge6, edges.next()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lte("2019-12-23 13:01:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); @@ -1158,25 +1158,25 @@ public void testOverrideEdgeWithRangeIndexAndTtl() { } // All edges are expired after 6s - edges = graph().traversal().E().has("date", "2019-12-23 14:00:00"); + edges = graph().traversal().E().hasLabel("read").has("date", "2019-12-23 14:00:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("date", "2019-12-23 14:01:00"); + edges = graph().traversal().E().hasLabel("read").has("date", "2019-12-23 14:01:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gt("2019-12-23 15:01:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gte("2019-12-23 15:01:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lt("2019-12-23 13:01:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lte("2019-12-23 13:01:00")); Assert.assertFalse(edges.hasNext()); } @@ -1210,31 +1210,31 @@ public void testOverrideEdgeWithShardIndexAndTtl() { "date", "2019-12-23 14:00:00"); graph().tx().commit(); - Iterator edges = graph().traversal().E().has("place", "home"); + Iterator edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge6, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:00:00"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge3, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge5, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge1, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); @@ -1265,38 +1265,38 @@ public void testOverrideEdgeWithShardIndexAndTtl() { graph().tx().commit(); // Due to overridden edges are expired, query will lead to async delete - edges = graph().traversal().E().has("place", "home"); + edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library"); + edges = graph().traversal().E().hasLabel("read").has("place", "library"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge12, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:00:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:01:00"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge9, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gt("2019-12-23 15:01:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge11, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gte("2019-12-23 15:01:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lt("2019-12-23 13:01:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge7, edges.next()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lte("2019-12-23 13:01:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); @@ -1309,33 +1309,33 @@ public void testOverrideEdgeWithShardIndexAndTtl() { } // All edges are expired after 6s - edges = graph().traversal().E().has("place", "home"); + edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library"); + edges = graph().traversal().E().hasLabel("read").has("place", "library"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:00:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:01:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); } @@ -1353,7 +1353,7 @@ public void testOverrideEdgeWithSearchIndexAndTtl() { "date", "2019-12-23 12:00:00"); graph().tx().commit(); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("place", Text.contains("school")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge, edges.next()); @@ -1370,10 +1370,10 @@ public void testOverrideEdgeWithSearchIndexAndTtl() { graph().tx().commit(); // Due to overridden edges are expired, query will lead to async delete - edges = graph().traversal().E().has("place", Text.contains("school")); + edges = graph().traversal().E().hasLabel("read").has("place", Text.contains("school")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", Text.contains("city")); + edges = graph().traversal().E().hasLabel("read").has("place", Text.contains("city")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(edge, edges.next()); graph().tx().commit(); @@ -1385,7 +1385,7 @@ public void testOverrideEdgeWithSearchIndexAndTtl() { } // All edges are expired after 6s - edges = graph().traversal().E().has("place", Text.contains("library")); + edges = graph().traversal().E().hasLabel("read").has("place", Text.contains("library")); Assert.assertFalse(edges.hasNext()); } @@ -1445,7 +1445,7 @@ public void testQueryEdgeWithSecondaryIndexAndTtlInTx() { baby.addEdge("read", java2, "place", "library of school", "date", "2019-12-23 12:00:00"); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("place", "library of school"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); @@ -1458,7 +1458,7 @@ public void testQueryEdgeWithSecondaryIndexAndTtlInTx() { } // All edges are expired after 3s - edges = graph().traversal().E().has("place", "library of school"); + edges = graph().traversal().E().hasLabel("read").has("place", "library of school"); Assert.assertFalse(edges.hasNext()); } @@ -1503,27 +1503,27 @@ public void testQueryEdgeWithRangeIndexAndTtlInTx() { baby.addEdge("read", java10, "place", "library of school", "date", "2019-12-23 16:00:00"); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("date", "2019-12-23 14:00:00"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(4, IteratorUtils.count(edges)); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(4, IteratorUtils.count(edges)); @@ -1536,22 +1536,22 @@ public void testQueryEdgeWithRangeIndexAndTtlInTx() { } // All edges are expired after 3s - edges = graph().traversal().E().has("date", "2019-12-23 14:00:00"); + edges = graph().traversal().E().hasLabel("read").has("date", "2019-12-23 14:00:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E() + edges = graph().traversal().E().hasLabel("read") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); } @@ -1603,31 +1603,31 @@ public void testQueryEdgeWithShardIndexAndTtlInTx() { baby.addEdge("read", java12, "place", "home", "date", "2019-12-23 14:00:00"); - Iterator edges = graph().traversal().E().has("place", "home"); + Iterator edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:00:00"); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(4, IteratorUtils.count(edges)); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(4, IteratorUtils.count(edges)); @@ -1640,26 +1640,26 @@ public void testQueryEdgeWithShardIndexAndTtlInTx() { } // All edges are expired after 3s - edges = graph().traversal().E().has("place", "home"); + edges = graph().traversal().E().hasLabel("read").has("place", "home"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", "2019-12-23 14:00:00"); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gt("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.gte("2019-12-23 15:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lt("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); - edges = graph().traversal().E().has("place", "library of school") + edges = graph().traversal().E().hasLabel("read").has("place", "library of school") .has("date", P.lte("2019-12-23 13:00:00")); Assert.assertFalse(edges.hasNext()); } @@ -1682,7 +1682,7 @@ public void testQueryEdgeWithSearchIndexAndTtlInTx() { baby.addEdge("read", java2, "place", "library of school", "date", "2019-12-23 12:00:00"); - Iterator edges = graph().traversal().E() + Iterator edges = graph().traversal().E().hasLabel("read") .has("place", Text.contains("library")); Assert.assertTrue(edges.hasNext()); Assert.assertEquals(2, IteratorUtils.count(edges)); @@ -1695,7 +1695,7 @@ public void testQueryEdgeWithSearchIndexAndTtlInTx() { } // All edges are expired after 3s - edges = graph().traversal().E().has("place", Text.contains("library")); + edges = graph().traversal().E().hasLabel("read").has("place", Text.contains("library")); Assert.assertFalse(edges.hasNext()); } @@ -4729,10 +4729,10 @@ public void testQueryByLongPropOfOverrideEdge() { louise.addEdge("strike", sean, "id", 1, "timestamp", current + 1, "place", "park", "tool", "shovel", "reason", "jeer", "arrested", false); - List edges = graph.traversal().E().has("timestamp", current) + List edges = graph.traversal().E().hasLabel("strike").has("timestamp", current) .toList(); Assert.assertEquals(0, edges.size()); - edges = graph.traversal().E().has("timestamp", current + 1).toList(); + edges = graph.traversal().E().hasLabel("strike").has("timestamp", current + 1).toList(); Assert.assertEquals(1, edges.size()); } @@ -4758,36 +4758,36 @@ public void testQueryByNegativeLongProperty() { graph.tx().commit(); - List edges = graph.traversal().E() + List edges = graph.traversal().E().hasLabel("transfer") .has("timestamp", -100L).toList(); Assert.assertEquals(1, edges.size()); Assert.assertEquals(IdGenerator.of(2), edges.get(0).value("id")); - edges = graph.traversal().E() + edges = graph.traversal().E().hasLabel("transfer") .has("timestamp", P.between(-101L, 0L)) .toList(); Assert.assertEquals(1, edges.size()); Assert.assertEquals(IdGenerator.of(2), edges.get(0).value("id")); - edges = graph.traversal().E().has("timestamp", P.gt(-101L)).toList(); + edges = graph.traversal().E().hasLabel("transfer").has("timestamp", P.gt(-101L)).toList(); Assert.assertEquals(2, edges.size()); - edges = graph.traversal().E().has("timestamp", P.gte(-100L)).toList(); + edges = graph.traversal().E().hasLabel("transfer").has("timestamp", P.gte(-100L)).toList(); Assert.assertEquals(2, edges.size()); - edges = graph.traversal().E().has("timestamp", P.gt(-100L)).toList(); + edges = graph.traversal().E().hasLabel("transfer").has("timestamp", P.gt(-100L)).toList(); Assert.assertEquals(1, edges.size()); Assert.assertEquals(IdGenerator.of(1), edges.get(0).value("id")); - edges = graph.traversal().E().has("timestamp", P.gt(-99L)).toList(); + edges = graph.traversal().E().hasLabel("transfer").has("timestamp", P.gt(-99L)).toList(); Assert.assertEquals(1, edges.size()); Assert.assertEquals(IdGenerator.of(1), edges.get(0).value("id")); - edges = graph.traversal().E().has("timestamp", P.lt(-100L)).toList(); + edges = graph.traversal().E().hasLabel("transfer").has("timestamp", P.lt(-100L)).toList(); Assert.assertEquals(0, edges.size()); - edges = graph.traversal().E().has("timestamp", P.lte(-100L)).toList(); + edges = graph.traversal().E().hasLabel("transfer").has("timestamp", P.lte(-100L)).toList(); Assert.assertEquals(1, edges.size()); - edges = graph.traversal().E().has("timestamp", P.lt(0L)).toList(); + edges = graph.traversal().E().hasLabel("transfer").has("timestamp", P.lt(0L)).toList(); Assert.assertEquals(1, edges.size()); } @@ -4807,17 +4807,17 @@ public void testQueryByRepeatedRangePredicates() { "timestamp", -4L, "message", "test"); graph.tx().commit(); - List edges = graph.traversal().E() + List edges = graph.traversal().E().hasLabel("transfer") .has("timestamp", -4L) .has("timestamp", P.lte(4L)).toList(); Assert.assertEquals(1, edges.size()); - long count = graph.traversal().E() + long count = graph.traversal().E().hasLabel("transfer") .has("timestamp", -4L) .has("timestamp", P.lte(4L)).count().next(); Assert.assertEquals(1L, count); - count = graph.traversal().E() + count = graph.traversal().E().hasLabel("transfer") .has("timestamp", P.lte(4L)) .has("timestamp", -4L).count().next(); Assert.assertEquals(1L, count); @@ -7081,10 +7081,10 @@ public void testQueryEdgeByStringPropOfOverrideEdge() { louise.addEdge("strike", sean, "id", 1, "timestamp", current, "place", "street", "tool", "shovel", "reason", "jeer", "arrested", false); - List edges = graph.traversal().E().has("place", "park") + List edges = graph.traversal().E().hasLabel("strike").has("place", "park") .toList(); Assert.assertEquals(0, edges.size()); - edges = graph.traversal().E().has("place", "street").toList(); + edges = graph.traversal().E().hasLabel("strike").has("place", "street").toList(); Assert.assertEquals(1, edges.size()); } @@ -7103,28 +7103,28 @@ public void testQueryEdgeBeforeAfterUpdateMultiPropertyWithIndex() { "tool", "shovel", "reason", "jeer", "arrested", false); - List vl = graph.traversal().E().has("tool", "shovel").toList(); + List vl = graph.traversal().E().hasLabel("strike").has("tool", "shovel").toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals(1, (int) vl.get(0).value("id")); - vl = graph.traversal().E().has("timestamp", current).toList(); + vl = graph.traversal().E().hasLabel("strike").has("timestamp", current).toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals(1, (int) vl.get(0).value("id")); - vl = graph.traversal().E().has("tool", "knife").toList(); + vl = graph.traversal().E().hasLabel("strike").has("tool", "knife").toList(); Assert.assertEquals(0, vl.size()); - vl = graph.traversal().E().has("timestamp", 666L).toList(); + vl = graph.traversal().E().hasLabel("strike").has("timestamp", 666L).toList(); Assert.assertEquals(0, vl.size()); edge.property("tool", "knife"); edge.property("timestamp", 666L); - vl = graph.traversal().E().has("tool", "shovel").toList(); + vl = graph.traversal().E().hasLabel("strike").has("tool", "shovel").toList(); Assert.assertEquals(0, vl.size()); - vl = graph.traversal().E().has("timestamp", current).toList(); + vl = graph.traversal().E().hasLabel("strike").has("timestamp", current).toList(); Assert.assertEquals(0, vl.size()); - vl = graph.traversal().E().has("tool", "knife").toList(); + vl = graph.traversal().E().hasLabel("strike").has("tool", "knife").toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals(1, (int) vl.get(0).value("id")); - vl = graph.traversal().E().has("timestamp", 666L).toList(); + vl = graph.traversal().E().hasLabel("strike").has("timestamp", 666L).toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals(1, (int) vl.get(0).value("id")); } @@ -7175,18 +7175,18 @@ public void testQueryEdgeBeforeAfterUpdatePropertyWithRangeIndex() { "tool", "shovel", "reason", "jeer", "arrested", false); - List el = graph.traversal().E().has("timestamp", current) + List el = graph.traversal().E().hasLabel("strike").has("timestamp", current) .toList(); Assert.assertEquals(1, el.size()); Assert.assertEquals(1, (int) el.get(0).value("id")); - el = graph.traversal().E().has("timestamp", 666L).toList(); + el = graph.traversal().E().hasLabel("strike").has("timestamp", 666L).toList(); Assert.assertEquals(0, el.size()); edge.property("timestamp", 666L); - el = graph.traversal().E().has("timestamp", current).toList(); + el = graph.traversal().E().hasLabel("strike").has("timestamp", current).toList(); Assert.assertEquals(0, el.size()); - el = graph.traversal().E().has("timestamp", 666L).toList(); + el = graph.traversal().E().hasLabel("strike").has("timestamp", 666L).toList(); Assert.assertEquals(1, el.size()); Assert.assertEquals(1, (int) el.get(0).value("id")); } @@ -7205,11 +7205,11 @@ public void testQueryEdgeWithNullablePropertyInCompositeIndex() { "timestamp", current, "place", "park", "tool", "shovel", "arrested", false); - List el = graph.traversal().E().has("place", "park") + List el = graph.traversal().E().hasLabel("strike").has("place", "park") .toList(); Assert.assertEquals(1, el.size()); Assert.assertEquals(1, (int) el.get(0).value("id")); - el = graph.traversal().E().has("place", "park") + el = graph.traversal().E().hasLabel("strike").has("place", "park") .has("tool", "shovel").toList(); Assert.assertEquals(1, el.size()); Assert.assertEquals(1, (int) el.get(0).value("id")); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java new file mode 100644 index 0000000000..534db06a0d --- /dev/null +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java @@ -0,0 +1,362 @@ +/* + * 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.hugegraph.core; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.apache.hugegraph.backend.page.PageInfo; +import org.apache.hugegraph.backend.query.Condition; +import org.apache.hugegraph.backend.query.ConditionQuery; +import org.apache.hugegraph.exception.NoIndexException; +import org.apache.hugegraph.schema.IndexLabel; +import org.apache.hugegraph.schema.SchemaManager; +import org.apache.hugegraph.testutil.Assert; +import org.apache.hugegraph.traversal.optimize.Text; +import org.apache.hugegraph.type.HugeType; +import org.apache.hugegraph.type.define.SchemaStatus; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.collect.ImmutableSet; + +public class SourceIndexCoverageTest extends BaseCoreTest { + + @Before + public void initSchema() { + SchemaManager schema = graph().schema(); + schema.propertyKey("coverageName").asText().create(); + schema.propertyKey("coverageScore").asInt().create(); + schema.propertyKey("coverageBody").asText().create(); + schema.vertexLabel("coverageA") + .properties("coverageName", "coverageScore", "coverageBody") + .useAutomaticId().create(); + schema.vertexLabel("coverageB") + .properties("coverageName", "coverageScore", "coverageBody") + .enableLabelIndex(false).useAutomaticId().create(); + schema.vertexLabel("unrelated").properties("coverageBody").useAutomaticId().create(); + } + + private Set addVertices() { + Vertex a = graph().addVertex(T.label, "coverageA", "coverageName", "same", + "coverageScore", 10, "coverageBody", "gold silver"); + Vertex b = graph().addVertex(T.label, "coverageB", "coverageName", "same", + "coverageScore", 20, "coverageBody", "gold bronze"); + this.commitTx(); + return ImmutableSet.of(a, b); + } + + private void nameIndex(String label) { + graph().schema().indexLabel(label + "ByName").onV(label) + .by("coverageName").secondary().create(); + } + + @Test + public void testCompleteCandidatesOrExplicitFailure() { + this.nameIndex("coverageA"); + Set expected = this.addVertices(); + Set actual; + try { + actual = graph().traversal().V().has("coverageName", "same").toSet(); + } catch (NoIndexException e) { + return; + } + Assert.assertEquals(expected, actual); + } + + @Test + public void testPartialCoverageRejectsIncompleteSource() { + this.nameIndex("coverageA"); + Set expected = this.addVertices(); + Assert.assertThrows(NoIndexException.class, () -> + graph().traversal().V().has("coverageName", "same").toSet(), error -> { + Assert.assertContains("Incomplete index coverage", error.getMessage()); + Assert.assertFalse(error.getMessage().contains("coverageB")); + }); + Assert.assertEquals(1, graph().traversal().V().hasLabel("coverageA") + .has("coverageName", "same").toList().size()); + this.nameIndex("coverageB"); + Assert.assertEquals(expected, + graph().traversal().V().has("coverageName", "same").toSet()); + } + + @Test + public void testEmptyCandidateLabelStillRequiresCoverage() { + this.nameIndex("coverageA"); + graph().addVertex(T.label, "coverageA", "coverageName", "same", + "coverageScore", 10, "coverageBody", "gold"); + this.commitTx(); + Assert.assertThrows(NoIndexException.class, () -> + graph().traversal().V().has("coverageName", "same").toList()); + } + + @Test + public void testNoCoverageAndExplicitIds() { + Set expected = this.addVertices(); + Assert.assertThrows(NoIndexException.class, () -> + graph().traversal().V().has("coverageName", "same").toList()); + Object[] ids = expected.stream().map(Vertex::id).toArray(); + Assert.assertEquals(expected, + graph().traversal().V(ids).has("coverageName", "same").toSet()); + graph().schema().edgeLabel("coverageLink").link("coverageA", "coverageB").create(); + Vertex a = expected.stream().filter(v -> v.label().equals("coverageA")).findFirst().get(); + Vertex b = expected.stream().filter(v -> v.label().equals("coverageB")).findFirst().get(); + a.addEdge("coverageLink", b); + this.commitTx(); + Assert.assertEquals(ImmutableSet.of(b), graph().traversal().V(a.id()) + .out("coverageLink").has("coverageName", "same").toSet()); + } + + @Test + public void testDownstreamStepsCannotHidePartialCoverage() { + this.nameIndex("coverageA"); + this.addVertices(); + for (GraphTraversal query : new GraphTraversal[]{ + graph().traversal().V().has("coverageName", "same").limit(1), + graph().traversal().V().has("coverageName", "same").count(), + graph().traversal().V().has("coverageName", "same").out(), + graph().traversal().V().has("coverageName", "same").where(__.out()), + graph().traversal().V().has("coverageName", "same").barrier()}) { + Assert.assertThrows(NoIndexException.class, query::toList); + } + } + + @Test + public void testPreparedTraversalChecksCurrentSchema() { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + this.addVertices(); + GraphTraversal query = graph().traversal().V().has("coverageName", "same"); + query.asAdmin().applyStrategies(); + graph().schema().vertexLabel("coverageLater").properties("coverageName") + .useAutomaticId().create(); + graph().addVertex(T.label, "coverageLater", "coverageName", "same"); + this.commitTx(); + Assert.assertThrows(NoIndexException.class, query::toList); + this.nameIndex("coverageLater"); + Assert.assertEquals(3, graph().traversal().V().has("coverageName", "same").toList().size()); + graph().schema().indexLabel("coverageBByName").remove(); + Assert.assertThrows(NoIndexException.class, () -> + graph().traversal().V().has("coverageName", "same").toList()); + } + + @Test + public void testRangeRequiresCompatibleCoverage() { + SchemaManager schema = graph().schema(); + schema.indexLabel("coverageAByScore").onV("coverageA").by("coverageScore").range().create(); + schema.indexLabel("coverageBByScore").onV("coverageB") + .by("coverageScore").secondary().create(); + Set expected = this.addVertices(); + Assert.assertThrows(NoIndexException.class, () -> + graph().traversal().V().has("coverageScore", P.gte(10)).toList()); + // Equality is supported by both RANGE and SECONDARY indexes. + Assert.assertEquals(1, graph().traversal().V().has("coverageScore", 10).toList().size()); + schema.indexLabel("coverageBByScore").remove(); + schema.indexLabel("coverageBRange").onV("coverageB").by("coverageScore").range().create(); + Assert.assertEquals(expected, + graph().traversal().V().has("coverageScore", P.gte(10)).toSet()); + } + + @Test + public void testSearchRequiresCompatibleCoverage() { + SchemaManager schema = graph().schema(); + schema.indexLabel("coverageASearch").onV("coverageA").by("coverageBody").search().create(); + schema.indexLabel("coverageBSecondary").onV("coverageB") + .by("coverageBody").secondary().create(); + this.addVertices(); + Assert.assertThrows(NoIndexException.class, () -> + graph().traversal().V().has("coverageBody", Text.contains("(gold)")).toList()); + schema.indexLabel("coverageBSearch").onV("coverageB").by("coverageBody").search().create(); + schema.indexLabel("unrelatedSearch").onV("unrelated").by("coverageBody").search().create(); + Assert.assertEquals(2, graph().traversal().V() + .has("coverageBody", Text.contains("(gold)")).toList().size()); + } + + @Test + public void testCompositeCoverage() { + SchemaManager schema = graph().schema(); + schema.indexLabel("coverageAComposite").onV("coverageA") + .by("coverageName", "coverageScore").secondary().create(); + this.nameIndex("coverageB"); + this.addVertices(); + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().V() + .has("coverageName", "same").has("coverageScore", 10).toList()); + schema.indexLabel("coverageBComposite").onV("coverageB") + .by("coverageName", "coverageScore").secondary().create(); + Assert.assertEquals(1, graph().traversal().V().has("coverageName", "same") + .has("coverageScore", 10).toList().size()); + } + + @Test + public void testJointIndexCoverage() { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + SchemaManager schema = graph().schema(); + schema.indexLabel("coverageARange").onV("coverageA") + .by("coverageScore").range().create(); + Set vertices = this.addVertices(); + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().V() + .has("coverageName", "same").has("coverageScore", P.gte(15)).toList()); + schema.indexLabel("coverageBRange").onV("coverageB") + .by("coverageScore").range().create(); + Vertex expected = vertices.stream().filter(v -> v.label().equals("coverageB")) + .findFirst().get(); + Assert.assertEquals(ImmutableSet.of(expected), graph().traversal().V() + .has("coverageName", "same").has("coverageScore", P.gte(15)).toSet()); + } + + @Test + public void testIndexStatusAndRebuild() { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + Set expected = this.addVertices(); + graph().schema().indexLabel("coverageBByName").rebuild(); + Assert.assertEquals(expected, + graph().traversal().V().has("coverageName", "same").toSet()); + IndexLabel index = graph().schema().getIndexLabel("coverageBByName"); + try { + for (SchemaStatus status : new SchemaStatus[]{SchemaStatus.CREATING, + SchemaStatus.REBUILDING, + SchemaStatus.DELETING}) { + this.params().schemaTransaction().updateSchemaStatus(index, status); + Assert.assertThrows(IllegalArgumentException.class, () -> + graph().traversal().V().has("coverageName", "same").toList()); + } + } finally { + this.params().schemaTransaction().updateSchemaStatus(index, SchemaStatus.CREATED); + } + Assert.assertEquals(expected, + graph().traversal().V().has("coverageName", "same").toSet()); + } + + @Test + public void testGlobalEdgeCoverage() { + SchemaManager schema = graph().schema(); + schema.edgeLabel("edgeA").link("coverageA", "coverageB") + .properties("coverageName").create(); + schema.edgeLabel("edgeB").link("coverageA", "coverageB") + .properties("coverageName").enableLabelIndex(false).create(); + schema.indexLabel("edgeAByName").onE("edgeA").by("coverageName").secondary().create(); + this.addVertices(); + Iterator vertices = graph().traversal().V().toList().iterator(); + Vertex a = vertices.next(); + Vertex b = vertices.next(); + if (a.label().equals("coverageB")) { + Vertex swap = a; + a = b; + b = swap; + } + a.addEdge("edgeA", b, "coverageName", "same"); + a.addEdge("edgeB", b, "coverageName", "same"); + this.commitTx(); + Assert.assertThrows(NoIndexException.class, () -> + graph().traversal().E().has("coverageName", "same").toList()); + schema.indexLabel("edgeBByName").onE("edgeB").by("coverageName").secondary().create(); + Assert.assertEquals(2, + graph().traversal().E().has("coverageName", "same").toList().size()); + } + + @Test + public void testParentEdgeIndexCoversSubLabels() { + Assume.assumeTrue(storeFeatures().supportsFatherAndSubEdgeLabel()); + SchemaManager schema = graph().schema(); + schema.edgeLabel("parentEdge").asBase().properties("coverageName", "coverageScore") + .multiTimes().sortKeys("coverageScore").create(); + for (String label : new String[]{"subA", "subB"}) { + schema.edgeLabel(label).withBase("parentEdge").link("coverageA", "coverageB") + .properties("coverageName", "coverageScore") + .multiTimes().sortKeys("coverageScore").create(); + } + schema.indexLabel("parentByName").onE("parentEdge") + .by("coverageName").secondary().create(); + Set vertices = this.addVertices(); + Vertex a = vertices.stream().filter(v -> v.label().equals("coverageA")).findFirst().get(); + Vertex b = vertices.stream().filter(v -> v.label().equals("coverageB")).findFirst().get(); + a.addEdge("subA", b, "coverageName", "same", "coverageScore", 1); + a.addEdge("subB", b, "coverageName", "same", "coverageScore", 2); + this.commitTx(); + Assert.assertEquals(2, + graph().traversal().E().has("coverageName", "same").toList().size()); + schema.indexLabel("parentByName").remove(); + schema.indexLabel("subAByName").onE("subA").by("coverageName").secondary().create(); + Assert.assertThrows(NoIndexException.class, () -> + graph().traversal().E().has("coverageName", "same").toList()); + schema.indexLabel("subBByName").onE("subB").by("coverageName").secondary().create(); + Assert.assertEquals(2, + graph().traversal().E().has("coverageName", "same").toList().size()); + } + + @Test + public void testPagingRequiresCoverageOnEveryRequest() { + Assume.assumeTrue(storeFeatures().supportsQueryByPage()); + this.nameIndex("coverageA"); + Set expected = this.addVertices(); + Assert.assertThrows(NoIndexException.class, () -> this.page("")); + this.nameIndex("coverageB"); + Set actual = new HashSet<>(); + String page = ""; + String resume = null; + int pages = 0; + do { + ConditionQuery query = this.pageQuery(page); + Iterator iterator = graph().vertices(query); + try { + iterator.forEachRemaining(actual::add); + page = PageInfo.pageInfo(iterator); + if (resume == null) { + resume = page; + } + } finally { + CloseableIterator.closeIterator(iterator); + } + Assert.assertTrue(++pages < 10); + } while (page != null && !page.isEmpty()); + Assert.assertEquals(expected, actual); + Assert.assertNotNull(resume); + String continuation = resume; + graph().schema().indexLabel("coverageBByName").remove(); + Assert.assertThrows(NoIndexException.class, () -> this.page(continuation)); + } + + private ConditionQuery pageQuery(String page) { + ConditionQuery query = new ConditionQuery(HugeType.VERTEX); + query.query(Condition.eq(graph().propertyKey("coverageName").id(), "same")); + query.limit(1); + query.page(page); + return query; + } + + private void page(String page) { + Iterator iterator = graph().vertices(this.pageQuery(page)); + try { + while (iterator.hasNext()) { + iterator.next(); + } + } finally { + CloseableIterator.closeIterator(iterator); + } + } +} diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java index 335130ede5..3399394b78 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java @@ -1320,7 +1320,7 @@ public void testAddVertexWithSecondaryIndexAndTtl() { "age", 3, "city", "Beijing"); this.commitTx(); - Iterator vertices = graph().traversal().V() + Iterator vertices = graph().traversal().V().hasLabel("fan") .has("city", "Beijing"); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex, vertices.next()); @@ -1332,7 +1332,7 @@ public void testAddVertexWithSecondaryIndexAndTtl() { // Ignore } - vertices = graph().traversal().V().has("city", "Beijing"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing"); Assert.assertFalse(vertices.hasNext()); } @@ -1355,23 +1355,23 @@ public void testAddVertexWithRangeIndexAndTtl() { "age", 7, "city", "Beijing"); this.commitTx(); - Iterator vertices = graph().traversal().V().has("age", 5); + Iterator vertices = graph().traversal().V().hasLabel("fan").has("age", 5); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex3, vertices.next()); - vertices = graph().traversal().V().has("age", P.gt(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gt(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex5, vertices.next()); - vertices = graph().traversal().V().has("age", P.gte(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gte(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("age", P.lt(4)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lt(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex1, vertices.next()); - vertices = graph().traversal().V().has("age", P.lte(4)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lte(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); this.commitTx(); @@ -1382,19 +1382,19 @@ public void testAddVertexWithRangeIndexAndTtl() { // Ignore } - vertices = graph().traversal().V().has("age", 5); + vertices = graph().traversal().V().hasLabel("fan").has("age", 5); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.gt(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gt(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.gte(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gte(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.lt(4)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lt(4)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.lte(4)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lte(4)); Assert.assertFalse(vertices.hasNext()); } @@ -1419,32 +1419,32 @@ public void testAddVertexWithShardIndexAndTtl() { "age", 5, "city", "Shanghai"); this.commitTx(); - Iterator vertices = graph().traversal().V().has("city", + Iterator vertices = graph().traversal().V().hasLabel("fan").has("city", "Shanghai"); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex6, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 5); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex3, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gt(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex5, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gte(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lt(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex1, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lte(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); @@ -1456,26 +1456,26 @@ public void testAddVertexWithShardIndexAndTtl() { // Ignore } - vertices = graph().traversal().V().has("city", "Shanghai"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Shanghai"); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 5); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gt(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gte(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lt(4)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lte(4)); Assert.assertFalse(vertices.hasNext()); } @@ -1489,7 +1489,7 @@ public void testAddVertexWithSearchIndexAndTtl() { "age", 3, "city", "Beijing Haidian"); this.commitTx(); - Iterator vertices = graph().traversal().V().has( + Iterator vertices = graph().traversal().V().hasLabel("fan").has( "city", Text.contains("Haidian")); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex, vertices.next()); @@ -1501,7 +1501,7 @@ public void testAddVertexWithSearchIndexAndTtl() { // Ignore } - vertices = graph().traversal().V().has("city", + vertices = graph().traversal().V().hasLabel("fan").has("city", Text.contains("Haidian")); Assert.assertFalse(vertices.hasNext()); } @@ -1541,7 +1541,7 @@ public void testOverrideVertexWithSecondaryIndexAndTtl() { "age", 3, "city", "Beijing"); this.commitTx(); - Iterator vertices = graph().traversal().V() + Iterator vertices = graph().traversal().V().hasLabel("fan") .has("city", "Beijing"); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex, vertices.next()); @@ -1560,10 +1560,10 @@ public void testOverrideVertexWithSecondaryIndexAndTtl() { // Due to overridden vertices are expired, // query will lead to async delete - vertices = graph().traversal().V().has("city", "Beijing"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing"); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Shanghai"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Shanghai"); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex, vertices.next()); this.commitTx(); @@ -1575,9 +1575,9 @@ public void testOverrideVertexWithSecondaryIndexAndTtl() { } // All edges are expired after 6s - vertices = graph().traversal().V().has("city", "Beijing"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing"); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Shanghai"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Shanghai"); Assert.assertFalse(vertices.hasNext()); } @@ -1600,23 +1600,23 @@ public void testOverrideVertexWithRangeIndexAndTtl() { "age", 11, "city", "Beijing"); this.commitTx(); - Iterator vertices = graph().traversal().V().has("age", 7); + Iterator vertices = graph().traversal().V().hasLabel("fan").has("age", 7); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex3, vertices.next()); - vertices = graph().traversal().V().has("age", P.gt(9)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gt(9)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex5, vertices.next()); - vertices = graph().traversal().V().has("age", P.gte(9)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gte(9)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("age", P.lt(5)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lt(5)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex1, vertices.next()); - vertices = graph().traversal().V().has("age", P.lte(5)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lte(5)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); this.commitTx(); @@ -1645,26 +1645,26 @@ public void testOverrideVertexWithRangeIndexAndTtl() { * Due to overridden vertices are expired, * query will lead to async delete */ - vertices = graph().traversal().V().has("age", 7); + vertices = graph().traversal().V().hasLabel("fan").has("age", 7); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", 8); + vertices = graph().traversal().V().hasLabel("fan").has("age", 8); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex8, vertices.next()); - vertices = graph().traversal().V().has("age", P.gt(10)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gt(10)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex10, vertices.next()); - vertices = graph().traversal().V().has("age", P.gte(10)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gte(10)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("age", P.lt(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lt(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex6, vertices.next()); - vertices = graph().traversal().V().has("age", P.lte(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lte(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); this.commitTx(); @@ -1676,22 +1676,22 @@ public void testOverrideVertexWithRangeIndexAndTtl() { } // All edges are expired after 6s - vertices = graph().traversal().V().has("age", 7); + vertices = graph().traversal().V().hasLabel("fan").has("age", 7); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", 8); + vertices = graph().traversal().V().hasLabel("fan").has("age", 8); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.gt(10)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gt(10)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.gte(10)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gte(10)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.lt(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lt(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.lte(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lte(6)); Assert.assertFalse(vertices.hasNext()); } @@ -1716,32 +1716,32 @@ public void testOverrideVertexWithShardIndexAndTtl() { "age", 5, "city", "Shanghai"); this.commitTx(); - Iterator vertices = graph().traversal().V().has("city", + Iterator vertices = graph().traversal().V().hasLabel("fan").has("city", "Shanghai"); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex6, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 5); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex3, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gt(7)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex5, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gte(7)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lt(3)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex1, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lte(3)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); @@ -1773,38 +1773,38 @@ public void testOverrideVertexWithShardIndexAndTtl() { this.commitTx(); // Due to overridden vertices are expired, query will lead to async delete - vertices = graph().traversal().V().has("city", "Shanghai"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Shanghai"); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Shenzhen"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Shenzhen"); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex12, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 5); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 6); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex9, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gt(8)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex11, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gte(8)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lt(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex7, vertices.next()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lte(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); @@ -1817,33 +1817,33 @@ public void testOverrideVertexWithShardIndexAndTtl() { } // All vertices are expired after 6s - vertices = graph().traversal().V().has("city", "Shanghai"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Shanghai"); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Shenzhen"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Shenzhen"); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 5); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 6); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gt(8)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gte(8)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lt(4)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lte(4)); Assert.assertFalse(vertices.hasNext()); } @@ -1857,7 +1857,7 @@ public void testOverrideVertexWithSearchIndexAndTtl() { "age", 3, "city", "Beijing Haidian"); this.commitTx(); - Iterator vertices = graph().traversal().V().has( + Iterator vertices = graph().traversal().V().hasLabel("fan").has( "city", Text.contains("Haidian")); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex, vertices.next()); @@ -1874,11 +1874,11 @@ public void testOverrideVertexWithSearchIndexAndTtl() { this.commitTx(); // Due to overridden vertices are expired, query will lead to async delete - vertices = graph().traversal().V().has("city", + vertices = graph().traversal().V().hasLabel("fan").has("city", Text.contains("Haidian")); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", + vertices = graph().traversal().V().hasLabel("fan").has("city", Text.contains("Pudong")); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(vertex, vertices.next()); @@ -1891,7 +1891,7 @@ public void testOverrideVertexWithSearchIndexAndTtl() { } // All vertices are expired after 6s - vertices = graph().traversal().V().has("city", + vertices = graph().traversal().V().hasLabel("fan").has("city", Text.contains("Pudong")); Assert.assertFalse(vertices.hasNext()); } @@ -1940,7 +1940,7 @@ public void testQueryVertexWithSecondaryIndexAndTtlInTx() { graph().addVertex(T.label, "fan", "name", "Baby2", "age", 3, "city", "Beijing"); - Iterator vertices = graph().traversal().V() + Iterator vertices = graph().traversal().V().hasLabel("fan") .has("city", "Beijing"); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); @@ -1953,7 +1953,7 @@ public void testQueryVertexWithSecondaryIndexAndTtlInTx() { } // All vertices are expired after 3s - vertices = graph().traversal().V().has("city", "Beijing"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing"); Assert.assertFalse(vertices.hasNext()); } @@ -1986,23 +1986,23 @@ public void testQueryVertexWithRangeIndexAndTtlInTx() { graph().addVertex(T.label, "fan", "name", "Baby10", "age", 7, "city", "Beijing"); - Iterator vertices = graph().traversal().V().has("age", 5); + Iterator vertices = graph().traversal().V().hasLabel("fan").has("age", 5); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("age", P.gt(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gt(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("age", P.gte(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gte(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(4, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("age", P.lt(4)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lt(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("age", P.lte(4)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lte(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(4, IteratorUtils.count(vertices)); this.commitTx(); @@ -2014,19 +2014,19 @@ public void testQueryVertexWithRangeIndexAndTtlInTx() { } // All vertices are expired after 3s - vertices = graph().traversal().V().has("age", 5); + vertices = graph().traversal().V().hasLabel("fan").has("age", 5); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.gt(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gt(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.gte(6)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.gte(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.lt(4)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lt(4)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("age", P.lte(4)); + vertices = graph().traversal().V().hasLabel("fan").has("age", P.lte(4)); Assert.assertFalse(vertices.hasNext()); } @@ -2063,32 +2063,32 @@ public void testQueryVertexWithShardIndexAndTtlInTx() { graph().addVertex(T.label, "fan", "name", "Baby12", "age", 5, "city", "Shanghai"); - Iterator vertices = graph().traversal().V() + Iterator vertices = graph().traversal().V().hasLabel("fan") .has("city", "Shanghai"); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 5); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gt(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gte(6)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(4, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lt(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lte(4)); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(4, IteratorUtils.count(vertices)); @@ -2101,26 +2101,26 @@ public void testQueryVertexWithShardIndexAndTtlInTx() { } // All vertices are expired after 3s - vertices = graph().traversal().V().has("city", "Shanghai"); + vertices = graph().traversal().V().hasLabel("fan").has("city", "Shanghai"); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", 5); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gt(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.gte(6)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lt(4)); Assert.assertFalse(vertices.hasNext()); - vertices = graph().traversal().V().has("city", "Beijing") + vertices = graph().traversal().V().hasLabel("fan").has("city", "Beijing") .has("age", P.lte(4)); Assert.assertFalse(vertices.hasNext()); } @@ -2137,7 +2137,7 @@ public void testQueryVertexWithSearchIndexAndTtlInTx() { graph().addVertex(T.label, "fan", "name", "Baby2", "age", 3, "city", "Beijing Haidian"); - Iterator vertices = graph().traversal().V().has( + Iterator vertices = graph().traversal().V().hasLabel("fan").has( "city", Text.contains("Haidian")); Assert.assertTrue(vertices.hasNext()); Assert.assertEquals(2, IteratorUtils.count(vertices)); @@ -2150,7 +2150,7 @@ public void testQueryVertexWithSearchIndexAndTtlInTx() { } // All vertices are expired after 3s - vertices = graph().traversal().V().has("city", Text.contains("Haidian")); + vertices = graph().traversal().V().hasLabel("fan").has("city", Text.contains("Haidian")); Assert.assertFalse(vertices.hasNext()); } @@ -3621,15 +3621,15 @@ public void testQueryByIntPropWithDifferentDataType() { initPersonIndex(false); init5Persons(); - List vertices = graph.traversal().V().has("age", 21).toList(); + List vertices = graph.traversal().V().hasLabel("person").has("age", 21).toList(); Assert.assertEquals(1, vertices.size()); Assert.assertThrows(IllegalArgumentException.class, () -> { - graph.traversal().V().has("age", 21.0).toList(); + graph.traversal().V().hasLabel("person").has("age", 21.0).toList(); }); Assert.assertThrows(IllegalArgumentException.class, () -> { - graph.traversal().V().has("age", "21").toList(); + graph.traversal().V().hasLabel("person").has("age", "21").toList(); }); } @@ -3645,41 +3645,41 @@ public void testQueryByIntPropWithNegativeNumber() { "city", "Beijing", "age", -10, "birth", Utils.date("2029-01-01")); - List vertices = graph.traversal().V().has("age", -10).toList(); + List vertices = graph.traversal().V().hasLabel("person").has("age", -10).toList(); Assert.assertEquals(1, vertices.size()); assertContains(vertices, T.label, "person", "name", "Sean", "city", "Beijing", "age", -10, "birth", Utils.date("2029-01-01")); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("age", P.between(-11, 0)) .toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("Sean", vertices.get(0).value("name")); - vertices = graph.traversal().V().has("age", P.gt(-11)).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", P.gt(-11)).toList(); Assert.assertEquals(2, vertices.size()); - vertices = graph.traversal().V().has("age", P.gte(-10)).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", P.gte(-10)).toList(); Assert.assertEquals(2, vertices.size()); - vertices = graph.traversal().V().has("age", P.gt(-10)).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", P.gt(-10)).toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("Louise", vertices.get(0).value("name")); - vertices = graph.traversal().V().has("age", P.gt(-9)).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", P.gt(-9)).toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("Louise", vertices.get(0).value("name")); - vertices = graph.traversal().V().has("age", P.lt(-10)).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", P.lt(-10)).toList(); Assert.assertEquals(0, vertices.size()); - vertices = graph.traversal().V().has("age", P.lte(-10)).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", P.lte(-10)).toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("Sean", vertices.get(0).value("name")); - vertices = graph.traversal().V().has("age", P.lt(0)).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", P.lt(0)).toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("Sean", vertices.get(0).value("name")); } @@ -4935,9 +4935,9 @@ public void testQueryByIntPropOfOverrideVertex() { "city", "Hongkong", "age", 29); this.mayCommitTx(); - List vertices = graph.traversal().V().has("age", 28).toList(); + List vertices = graph.traversal().V().hasLabel("person").has("age", 28).toList(); Assert.assertEquals(0, vertices.size()); - vertices = graph.traversal().V().has("age", 29).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", 29).toList(); Assert.assertEquals(1, vertices.size()); } @@ -4952,10 +4952,10 @@ public void testQueryByStringPropOfOverrideVertex() { "city", "Hongkong", "age", 29); this.mayCommitTx(); - List vertices = graph.traversal().V().has("city", "Beijing") + List vertices = graph.traversal().V().hasLabel("person").has("city", "Beijing") .toList(); Assert.assertEquals(0, vertices.size()); - vertices = graph.traversal().V().has("city", "Hongkong").toList(); + vertices = graph.traversal().V().hasLabel("person").has("city", "Hongkong").toList(); Assert.assertEquals(1, vertices.size()); } @@ -5005,7 +5005,7 @@ public void testQueryWithTxNotCommittedByIntProp() { "age", 18, "city", "Beijing"); List vertices; - vertices = graph.traversal().V().has("age", 18).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", 18).toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals(18, (int) vertices.get(0).value("age")); @@ -5070,41 +5070,41 @@ public void testQueryByJointIndexes() { List vertices; // results count < 2 - vertices = graph.traversal().V().has("age", 3).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", 3).toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph.traversal().V().has("city", "Hongkong").toList(); + vertices = graph.traversal().V().hasLabel("person").has("city", "Hongkong").toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("city", "Hongkong").has("age", 2).toList(); Assert.assertEquals(0, vertices.size()); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("city", "Taipei").has("age", 3).toList(); Assert.assertEquals(0, vertices.size()); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("city", "Hongkong").has("age", 3).toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("city", "Hongkong").has("age", 3).toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("city", "Hongkong").has("age", 3) .skip(1).toList(); Assert.assertEquals(0, vertices.size()); // results count >= 2 - vertices = graph.traversal().V().has("age", 20).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", 20).toList(); Assert.assertEquals(2, vertices.size()); - vertices = graph.traversal().V().has("age", 19).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", 19).toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph.traversal().V().has("city", "Beijing").toList(); + vertices = graph.traversal().V().hasLabel("person").has("city", "Beijing").toList(); Assert.assertEquals(3, vertices.size()); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("city", "Beijing").has("age", P.lt(21)).toList(); Assert.assertEquals(3, vertices.size()); @@ -5117,11 +5117,11 @@ public void testQueryByJointIndexes() { Whitebox.setInternalState(tx, "indexTx.indexIntersectThresh", i); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("city", "Beijing").has("age", 20).toList(); Assert.assertEquals(2, vertices.size()); - vertices = graph.traversal().V() + vertices = graph.traversal().V().hasLabel("person") .has("city", "Beijing").has("age", 20) .skip(1).toList(); Assert.assertEquals(1, vertices.size()); @@ -5140,11 +5140,11 @@ public void testQueryByJointIndexesAndCompositeIndexForOneLabel() { this.commitTx(); List vertices; - vertices = graph().traversal().V().has("age", 3).toList(); + vertices = graph().traversal().V().hasLabel("person").has("age", 3).toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph().traversal().V().has("city", "Hongkong").toList(); + vertices = graph().traversal().V().hasLabel("person").has("city", "Hongkong").toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph().traversal().V().has("city", "Hongkong") + vertices = graph().traversal().V().hasLabel("person").has("city", "Hongkong") .has("age", 3).toList(); Assert.assertEquals(1, vertices.size()); @@ -5155,11 +5155,11 @@ public void testQueryByJointIndexesAndCompositeIndexForOneLabel() { graph().schema().indexLabel("personByCityAndAge").onV("person") .by("city", "age").ifNotExist().create(); - vertices = graph().traversal().V().has("age", 3).toList(); + vertices = graph().traversal().V().hasLabel("person").has("age", 3).toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph().traversal().V().has("city", "Hongkong").toList(); + vertices = graph().traversal().V().hasLabel("person").has("city", "Hongkong").toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph().traversal().V().has("city", "Hongkong") + vertices = graph().traversal().V().hasLabel("person").has("city", "Hongkong") .has("age", 3).toList(); Assert.assertEquals(1, vertices.size()); } @@ -5187,7 +5187,7 @@ public void testQueryByJointIndexesAndCompositeIndexForMultiLabel() { this.mayCommitTx(); List vertices; - vertices = graph().traversal().V().has("age", 3) + vertices = graph().traversal().V().hasLabel("dog", "cat").has("age", 3) .has("city", "Hongkong").toList(); Assert.assertEquals(2, vertices.size()); Set labels = new HashSet<>(); @@ -5211,7 +5211,7 @@ public void testQueryByJointIndexesOnlyWithCompositeIndex() { "city", "Hongkong", "age", 3); this.mayCommitTx(); - List vertices = graph().traversal().V().has("age", 3) + List vertices = graph().traversal().V().hasLabel("dog").has("age", 3) .has("city", "Hongkong") .has("name", "Tom").toList(); Assert.assertEquals(1, vertices.size()); @@ -5225,7 +5225,7 @@ public void testQueryByJointIndexesWithRangeIndex() { "city", "Hongkong", "age", 3); List vertices; - vertices = graph().traversal().V().has("age", P.gt(2)) + vertices = graph().traversal().V().hasLabel("person").has("age", P.gt(2)) .has("city", "Hongkong").toList(); Assert.assertEquals(1, vertices.size()); } @@ -5243,7 +5243,7 @@ public void testQueryByJointIndexesWithCompositeIndexIncludeOtherField() { "city", "Hongkong", "age", 3); this.mayCommitTx(); - List vertices = graph().traversal().V().has("age", P.gt(2)) + List vertices = graph().traversal().V().hasLabel("dog").has("age", P.gt(2)) .has("city", "Hongkong").toList(); Assert.assertEquals(1, vertices.size()); } @@ -5537,47 +5537,47 @@ public void testQueryByShardIndex() { "city", "Beijing", "age", 29); this.mayCommitTx(); - List vertices = graph().traversal().V() + List vertices = graph().traversal().V().hasLabel("person") .has("city", "Hongkong") .toList(); Assert.assertEquals(2, vertices.size()); - vertices = graph().traversal().V() + vertices = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .toList(); Assert.assertEquals(3, vertices.size()); - vertices = graph().traversal().V() + vertices = graph().traversal().V().hasLabel("person") .has("city", "Hongkong") .has("age", 15).toList(); Assert.assertEquals(1, vertices.size()); - vertices = graph().traversal().V() + vertices = graph().traversal().V().hasLabel("person") .has("city", "Hongkong") .has("age", P.between(10, 20)).toList(); Assert.assertEquals(2, vertices.size()); - vertices = graph().traversal().V() + vertices = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .has("age", P.between(20, 30)).toList(); Assert.assertEquals(3, vertices.size()); - vertices = graph().traversal().V() + vertices = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .has("age", P.lt(29)).toList(); Assert.assertEquals(2, vertices.size()); - vertices = graph().traversal().V() + vertices = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .has("age", P.lte(29)).toList(); Assert.assertEquals(3, vertices.size()); - vertices = graph().traversal().V() + vertices = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .has("age", P.gt(21)).toList(); Assert.assertEquals(2, vertices.size()); - vertices = graph().traversal().V() + vertices = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .has("age", P.gte(21)).toList(); Assert.assertEquals(3, vertices.size()); @@ -6392,15 +6392,15 @@ public void testAddVertexPropertyWithSpecialValueForSecondaryIndex() { List vertices; - vertices = graph.traversal().V().has("city", "b\u0001").toList(); + vertices = graph.traversal().V().hasLabel("person").has("city", "b\u0001").toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("1", vertices.get(0).value("name")); - vertices = graph.traversal().V().has("city", "c\u0002").toList(); + vertices = graph.traversal().V().hasLabel("person").has("city", "c\u0002").toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("2", vertices.get(0).value("name")); - vertices = graph.traversal().V().has("city", "d\u0003").toList(); + vertices = graph.traversal().V().hasLabel("person").has("city", "d\u0003").toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("3", vertices.get(0).value("name")); @@ -6423,7 +6423,7 @@ public void testAddVertexPropertyWithSpecialValueForSecondaryIndex() { "city", "a\u0000", "age", 0); this.commitTx(); - vertices = graph.traversal().V().has("city", "a\u0000").toList(); + vertices = graph.traversal().V().hasLabel("person").has("city", "a\u0000").toList(); Assert.assertEquals(1, vertices.size()); Assert.assertEquals("0", vertices.get(0).value("name")); } @@ -6594,7 +6594,7 @@ public void testQueryVertexByPropertyWithEmptyString() { graph.addVertex(T.label, "person", "name", "Baby", "city", ""); this.commitTx(); - Vertex vertex = graph.traversal().V().has("city", "").next(); + Vertex vertex = graph.traversal().V().hasLabel("person").has("city", "").next(); Assert.assertEquals("Baby", vertex.value("name")); Assert.assertEquals("", vertex.value("city")); } @@ -6689,29 +6689,29 @@ public void testQueryVertexBeforeAfterUpdateMultiPropertyWithIndex() { "city", "Hongkong", "age", 3); this.commitTx(); - List vl = graph.traversal().V().has("age", 3).toList(); + List vl = graph.traversal().V().hasLabel("person").has("age", 3).toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals("Baby", vl.get(0).value("name")); - vl = graph.traversal().V().has("city", "Hongkong").toList(); + vl = graph.traversal().V().hasLabel("person").has("city", "Hongkong").toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals("Baby", vl.get(0).value("name")); - vl = graph.traversal().V().has("age", 5).toList(); + vl = graph.traversal().V().hasLabel("person").has("age", 5).toList(); Assert.assertEquals(0, vl.size()); - vl = graph.traversal().V().has("city", "Shanghai").toList(); + vl = graph.traversal().V().hasLabel("person").has("city", "Shanghai").toList(); Assert.assertEquals(0, vl.size()); vertex.property("age", 5); vertex.property("city", "Shanghai"); this.commitTx(); - vl = graph.traversal().V().has("age", 3).toList(); + vl = graph.traversal().V().hasLabel("person").has("age", 3).toList(); Assert.assertEquals(0, vl.size()); - vl = graph.traversal().V().has("city", "Hongkong").toList(); + vl = graph.traversal().V().hasLabel("person").has("city", "Hongkong").toList(); Assert.assertEquals(0, vl.size()); - vl = graph.traversal().V().has("age", 5).toList(); + vl = graph.traversal().V().hasLabel("person").has("age", 5).toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals("Baby", vl.get(0).value("name")); - vl = graph.traversal().V().has("city", "Shanghai").toList(); + vl = graph.traversal().V().hasLabel("person").has("city", "Shanghai").toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals("Baby", vl.get(0).value("name")); } @@ -6725,18 +6725,18 @@ public void testQueryVertexBeforeAfterUpdatePropertyWithSecondaryIndex() { "city", "Hongkong", "age", 3); this.commitTx(); - List vl = graph.traversal().V().has("city", "Hongkong").toList(); + List vl = graph.traversal().V().hasLabel("person").has("city", "Hongkong").toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals("Baby", vl.get(0).value("name")); - vl = graph.traversal().V().has("city", "Shanghai").toList(); + vl = graph.traversal().V().hasLabel("person").has("city", "Shanghai").toList(); Assert.assertEquals(0, vl.size()); vertex.property("city", "Shanghai"); this.commitTx(); - vl = graph.traversal().V().has("city", "Hongkong").toList(); + vl = graph.traversal().V().hasLabel("person").has("city", "Hongkong").toList(); Assert.assertEquals(0, vl.size()); - vl = graph.traversal().V().has("city", "Shanghai").toList(); + vl = graph.traversal().V().hasLabel("person").has("city", "Shanghai").toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals("Baby", vl.get(0).value("name")); } @@ -6750,18 +6750,18 @@ public void testQueryVertexBeforeAfterUpdatePropertyWithRangeIndex() { "city", "Hongkong", "age", 3); this.commitTx(); - List vl = graph.traversal().V().has("age", 3).toList(); + List vl = graph.traversal().V().hasLabel("person").has("age", 3).toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals("Baby", vl.get(0).value("name")); - vl = graph.traversal().V().has("age", 5).toList(); + vl = graph.traversal().V().hasLabel("person").has("age", 5).toList(); Assert.assertEquals(0, vl.size()); vertex.property("age", 5); this.commitTx(); - vl = graph.traversal().V().has("age", 3).toList(); + vl = graph.traversal().V().hasLabel("person").has("age", 3).toList(); Assert.assertEquals(0, vl.size()); - vl = graph.traversal().V().has("age", 5).toList(); + vl = graph.traversal().V().hasLabel("person").has("age", 5).toList(); Assert.assertEquals(1, vl.size()); Assert.assertEquals("Baby", vl.get(0).value("name")); } @@ -8891,17 +8891,8 @@ public void testQueryByUnionIndexInPageWithSomeIndexNoData() { storeFeatures().supportsQueryByPage()); HugeGraph graph = graph(); - SchemaManager schema = graph.schema(); GraphTraversalSource g = graph.traversal(); - initPageTestData(); - - // Author has index by name but no data - schema.indexLabel("authorByName") - .onV("author") - .by("name") - .secondary() - .ifNotExist() - .create(); + initPageTestData(true); GraphTraversal iter = g.V().has("name", "marko") .has("~page", "").limit(1); @@ -8936,21 +8927,21 @@ public void testQueryBySecondaryIndexWithLimitAndOffset() { initPersonIndex(true); init5Persons(); - List vertices = graph().traversal().V() + List vertices = graph().traversal().V().hasLabel("person") .has("city", "Beijing").toList(); Assert.assertEquals(3, vertices.size()); - List verticesSkip = graph().traversal().V() + List verticesSkip = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .skip(1).toList(); Assert.assertEquals(2, verticesSkip.size()); - Set vertices1 = graph().traversal().V() + Set vertices1 = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .range(0, 2).toSet(); Assert.assertEquals(2, vertices1.size()); - Set vertices2 = graph().traversal().V() + Set vertices2 = graph().traversal().V().hasLabel("person") .has("city", "Beijing") .range(2, 3).toSet(); Assert.assertEquals(1, vertices2.size()); @@ -8965,20 +8956,20 @@ public void testQueryByRangeIndexWithLimitAndOffset() { initPersonIndex(false); init5Persons(); - List vertices = graph().traversal().V() + List vertices = graph().traversal().V().hasLabel("person") .has("age", P.between(5, 22)).toList(); Assert.assertEquals(4, vertices.size()); - List verticesSkip = graph().traversal().V() + List verticesSkip = graph().traversal().V().hasLabel("person") .has("age", P.between(5, 22)) .skip(1).toList(); Assert.assertEquals(3, verticesSkip.size()); - Set vertices1 = graph().traversal().V() + Set vertices1 = graph().traversal().V().hasLabel("person") .has("age", P.between(5, 22)) .range(0, 3).toSet(); Assert.assertEquals(3, vertices1.size()); - Set vertices2 = graph().traversal().V() + Set vertices2 = graph().traversal().V().hasLabel("person") .has("age", P.between(5, 22)) .range(3, 4).toSet(); Assert.assertEquals(1, vertices2.size()); @@ -9348,19 +9339,19 @@ public void testQueryBySearchIndexWithSpecialSymbol() { Assert.assertEquals(vertex6.value("city"), city); List vertices; - vertices = g.V().has("city", Text.contains("abc")).toList(); + vertices = g.V().hasLabel("person").has("city", Text.contains("abc")).toList(); Assert.assertEquals(3, vertices.size()); Assert.assertTrue(vertices.contains(vertex1)); Assert.assertTrue(vertices.contains(vertex3)); Assert.assertTrue(vertices.contains(vertex5)); - vertices = g.V().has("city", Text.contains("\u0002")).toList(); + vertices = g.V().hasLabel("person").has("city", Text.contains("\u0002")).toList(); Assert.assertEquals(0, vertices.size()); - vertices = g.V().has("city", Text.contains("\u0003")).toList(); + vertices = g.V().hasLabel("person").has("city", Text.contains("\u0003")).toList(); Assert.assertEquals(0, vertices.size()); - vertices = g.V().has("city", Text.contains("\u0001")).toList(); + vertices = g.V().hasLabel("person").has("city", Text.contains("\u0001")).toList(); Assert.assertEquals(0, vertices.size()); String backend = graph.backend(); @@ -9397,6 +9388,15 @@ public void testEnhanceTextMatch() { graph.schema().indexLabel("personByName").onV("person") .by("name").search().ifNotExist().create(); + // Keep the global SEARCH path: qualifying person would select its + // primary-key path instead. All candidate labels need SEARCH coverage. + for (VertexLabel label : graph.schema().getVertexLabels()) { + if (!label.name().equals("person") && + label.properties().contains(graph.propertyKey("name").id())) { + graph.schema().indexLabel(label.name() + "ByNameSearchCoverage") + .onV(label.name()).by("name").search().create(); + } + } Vertex vertex1 = graph.addVertex(T.label, "person", "name", "秦始皇", "city", "Hongkong", "age", 15); @@ -9534,8 +9534,25 @@ private void init5Computers() { } private void initPageTestData() { + this.initPageTestData(false); + } + + private void initPageTestData(boolean emptyIndexFirst) { SchemaManager schema = graph().schema(); + // These global paging tests require complete source-index coverage. + // Keep their two-label fixture separate from the unrelated labels + // created by initSchema(); partial coverage has dedicated regressions. + for (VertexLabel label : schema.getVertexLabels()) { + schema.vertexLabel(label.name()).remove(); + } schema.propertyKey("lang").asText().ifNotExist().create(); + if (emptyIndexFirst) { + // Keep the empty index ahead of the populated labels so the union + // test still exercises continuation past an empty first source. + schema.vertexLabel("author").properties("name").useAutomaticId().create(); + schema.indexLabel("authorByName").onV("author") + .by("name").secondary().create(); + } schema.vertexLabel("programmer") .properties("name", "age", "city") From f435a1f5891afc04549bc5e2723e7cde8472134b Mon Sep 17 00:00:00 2001 From: contrueCT Date: Mon, 28 Sep 2026 19:10:44 +0800 Subject: [PATCH 2/4] fix(core): preserve OLAP and invalid-value query behavior --- .../backend/tx/GraphIndexTransaction.java | 10 ++++- .../core/SourceIndexCoverageTest.java | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java index d84917470a..718917f39c 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java @@ -814,7 +814,10 @@ private Set collectMatchedIndexes(ConditionQuery query, matchedIndexes.add(index); } } - if (requireCompleteCoverage && !matchedIndexes.isEmpty()) { + // An invalid value returns no results after an index is found. Keep + // that result before deciding whether index coverage is complete. + if (requireCompleteCoverage && !matchedIndexes.isEmpty() && + validQueryConditionValues(this.graph(), query)) { this.checkIndexCoverage(query, schemaLabels, matchedIndexes); } return matchedIndexes; @@ -830,6 +833,11 @@ private void checkIndexCoverage(ConditionQuery query, // OLAP properties are shared rather than declared on every label. requiredProperties.removeIf(id -> this.graph().propertyKey(id).olap()); } + if (requiredProperties.isEmpty()) { + // A shared OLAP index can match several labels, while MatchedIndex + // deduplicates those matches by index labels alone. + return; + } Set covered = new HashSet<>(); for (MatchedIndex index : indexes) { covered.add(index.schemaLabel().id()); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java index 534db06a0d..c82d4d5711 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java @@ -30,7 +30,9 @@ import org.apache.hugegraph.testutil.Assert; import org.apache.hugegraph.traversal.optimize.Text; import org.apache.hugegraph.type.HugeType; +import org.apache.hugegraph.type.define.GraphReadMode; import org.apache.hugegraph.type.define.SchemaStatus; +import org.apache.hugegraph.type.define.WriteType; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; @@ -41,6 +43,7 @@ import org.junit.Before; import org.junit.Test; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; public class SourceIndexCoverageTest extends BaseCoreTest { @@ -103,6 +106,48 @@ public void testPartialCoverageRejectsIncompleteSource() { graph().traversal().V().has("coverageName", "same").toSet()); } + @Test + public void testInvalidValuePrecedesPartialCoverageError() { + this.addVertices(); + ConditionQuery invalid = new ConditionQuery(HugeType.VERTEX); + invalid.query(Condition.eq(graph().propertyKey("coverageScore").id(), "not-an-int")); + Assert.assertThrows(NoIndexException.class, () -> graph().vertices(invalid).hasNext()); + + graph().schema().indexLabel("coverageAByScore").onV("coverageA") + .by("coverageScore").secondary().create(); + Assert.assertEquals(ImmutableList.of(), ImmutableList.copyOf(graph().vertices(invalid))); + + ConditionQuery valid = new ConditionQuery(HugeType.VERTEX); + valid.query(Condition.eq(graph().propertyKey("coverageScore").id(), 10)); + Assert.assertThrows(NoIndexException.class, () -> graph().vertices(valid).hasNext()); + } + + @Test + public void testOlapOnlyQueryAcrossLabels() { + // The existing OLAP secondary-property query also returns no results + // on HStore master; its backend coverage is tracked in #3090. + Assume.assumeFalse("HStore OLAP secondary query is tracked in #3090", + "hstore".equals(graph().backend())); + Assume.assumeTrue("Not support olap properties", + storeFeatures().supportsOlapProperties()); + graph().schema().propertyKey("coverageOlap").asText() + .writeType(WriteType.OLAP_SECONDARY).create(); + Set expected = this.addVertices(); + for (Vertex vertex : expected) { + graph().addVertex(T.id, vertex.id(), "coverageOlap", "same"); + } + this.commitTx(); + + GraphReadMode previous = graph().readMode(); + graph().readMode(GraphReadMode.ALL); + try { + Assert.assertEquals(expected, + graph().traversal().V().has("coverageOlap", "same").toSet()); + } finally { + graph().readMode(previous); + } + } + @Test public void testEmptyCandidateLabelStillRequiresCoverage() { this.nameIndex("coverageA"); From 0de33eda10ccc07d87a0ed0b9980996e8c31aa89 Mon Sep 17 00:00:00 2001 From: contrueCT Date: Tue, 29 Sep 2026 14:02:41 +0800 Subject: [PATCH 3/4] fix(test): complete TinkerPop fixture index coverage --- .../core/SourceIndexCoverageTest.java | 62 ++++++++++++++++--- .../apache/hugegraph/tinkerpop/TestGraph.java | 28 +++++++++ 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java index c82d4d5711..98c9098ab7 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java @@ -28,14 +28,17 @@ import org.apache.hugegraph.schema.IndexLabel; import org.apache.hugegraph.schema.SchemaManager; import org.apache.hugegraph.testutil.Assert; +import org.apache.hugegraph.tinkerpop.TestGraph; import org.apache.hugegraph.traversal.optimize.Text; import org.apache.hugegraph.type.HugeType; import org.apache.hugegraph.type.define.GraphReadMode; +import org.apache.hugegraph.type.define.IdStrategy; import org.apache.hugegraph.type.define.SchemaStatus; import org.apache.hugegraph.type.define.WriteType; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator; @@ -78,16 +81,59 @@ private void nameIndex(String label) { } @Test - public void testCompleteCandidatesOrExplicitFailure() { + public void testCompleteCandidatesReturnAllResults() { this.nameIndex("coverageA"); + this.nameIndex("coverageB"); Set expected = this.addVertices(); - Set actual; - try { - actual = graph().traversal().V().has("coverageName", "same").toSet(); - } catch (NoIndexException e) { - return; - } - Assert.assertEquals(expected, actual); + Assert.assertEquals(expected, + graph().traversal().V().has("coverageName", "same").toSet()); + } + + @Test + public void testTinkerPopModernSchemaCoversGlobalPropertyQueries() { + new TestGraph(graph()).initModernSchema(IdStrategy.AUTOMATIC); + Vertex person = graph().addVertex(T.label, "person", "name", "marko", "age", 29); + Vertex software = graph().addVertex(T.label, "software", "name", "lop"); + Edge created = person.addEdge("created", software, "weight", 0.4d); + this.commitTx(); + + Assert.assertEquals(ImmutableSet.of(person), + graph().traversal().V().has("name", "marko").toSet()); + Assert.assertEquals(ImmutableSet.of(person), + graph().traversal().V().has("age", 29).toSet()); + Assert.assertEquals(ImmutableSet.of(created), + graph().traversal().E().has("weight", 0.4d).toSet()); + } + + @Test + public void testTinkerPopBasicSchemaCoversSharedProperties() { + new TestGraph(graph()).initBasicSchema(IdStrategy.AUTOMATIC, TestGraph.DEFAULT_VL); + Vertex vertex = graph().addVertex(T.label, TestGraph.DEFAULT_VL, "name", "shared"); + Vertex person = graph().addVertex(T.label, "person", "name", "shared"); + Vertex software = graph().addVertex(T.label, "software", "name", "shared"); + Edge self = vertex.addEdge("self", vertex, "name", "shared"); + Edge friend = vertex.addEdge("friend", vertex, "name", "shared"); + Edge link = vertex.addEdge("l", vertex, "name", "shared"); + Edge partition = vertex.addEdge("aTOa", vertex, + "gremlin.partitionGraphStrategy.partition", "shared"); + this.commitTx(); + + Assert.assertEquals(ImmutableSet.of(vertex, person, software), + graph().traversal().V().has("name", "shared").toSet()); + Assert.assertEquals(ImmutableSet.of(self, friend, link), + graph().traversal().E().has("name", "shared").toSet()); + Assert.assertEquals(ImmutableSet.of(partition), graph().traversal().E() + .has("gremlin.partitionGraphStrategy.partition", "shared").toSet()); + } + + @Test + public void testTinkerPopSinkSchemaCoversName() { + new TestGraph(graph()).initSinkSchema(); + Vertex message = graph().addVertex(T.label, "message", "name", "ping"); + this.commitTx(); + + Assert.assertEquals(ImmutableSet.of(message), + graph().traversal().V().has("name", "ping").toSet()); } @Test diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/TestGraph.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/TestGraph.java index 53dc1fe9ac..31a035a6eb 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/TestGraph.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/TestGraph.java @@ -533,6 +533,10 @@ public void initModernSchema(IdStrategy idStrategy) { .ifNotExist().create(); schema.indexLabel("dogByName").onV("dog").by("name") .ifNotExist().create(); + schema.indexLabel("animalByName").onV("animal").by("name") + .ifNotExist().create(); + schema.indexLabel("animalByAge").onV("animal").by("age").range() + .ifNotExist().create(); schema.indexLabel("vertexByName").onV("vertex").by("name") .ifNotExist().create(); schema.indexLabel("vertexByAge").onV("vertex").by("age").range() @@ -541,6 +545,8 @@ public void initModernSchema(IdStrategy idStrategy) { .ifNotExist().create(); schema.indexLabel("createdByWeight").onE("created").by("weight") .range().ifNotExist().create(); + schema.indexLabel("createdByEdgeWeight").onE("createdBy").by("weight") + .range().ifNotExist().create(); schema.indexLabel("personByNameAge").onV("person").by("name", "age") .ifNotExist().create(); schema.indexLabel("vertexByP").onV("vertex").by("p") @@ -855,6 +861,16 @@ private void initBasicVertexLabelAndEdgeLabelExceptV(String defaultVL) { schema.indexLabel("selfByName").onE("self").by("name") .ifNotExist().create(); + if (!"person".equals(defaultVL)) { + schema.indexLabel("personByName").onV("person").by("name") + .ifNotExist().create(); + } + schema.indexLabel("softwareByName").onV("software").by("name") + .ifNotExist().create(); + schema.indexLabel("friendByName").onE("friend").by("name") + .ifNotExist().create(); + schema.indexLabel("lByName").onE("l").by("name") + .ifNotExist().create(); schema.indexLabel("selfBy__id").onE("self").by("__id") .ifNotExist().create(); schema.indexLabel("selfBySome").onE("self").by("some") @@ -869,12 +885,21 @@ private void initBasicVertexLabelAndEdgeLabelExceptV(String defaultVL) { schema.indexLabel("aTOaByGremlinPartition").onE("aTOa") .by("gremlin.partitionGraphStrategy.partition") .ifNotExist().create(); + schema.indexLabel("aTObByGremlinPartition").onE("aTOb") + .by("gremlin.partitionGraphStrategy.partition") + .ifNotExist().create(); schema.indexLabel("aTOcByGremlinPartition").onE("aTOc") .by("gremlin.partitionGraphStrategy.partition") .ifNotExist().create(); schema.indexLabel("bTOcByGremlinPartition").onE("bTOc") .by("gremlin.partitionGraphStrategy.partition") .ifNotExist().create(); + schema.indexLabel("connectsToByGremlinPartition").onE("connectsTo") + .by("gremlin.partitionGraphStrategy.partition") + .ifNotExist().create(); + schema.indexLabel("relatesToByGremlinPartition").onE("relatesTo") + .by("gremlin.partitionGraphStrategy.partition") + .ifNotExist().create(); schema.edgeLabel("blah1").link(defaultVL, defaultVL) .ifNotExist().create(); schema.edgeLabel("blah2").link(defaultVL, defaultVL) @@ -942,5 +967,8 @@ public void initSinkSchema() { schema.indexLabel("loopsByName").onV("loops") .secondary().by("name") .ifNotExist().create(); + schema.indexLabel("messageByName").onV("message") + .secondary().by("name") + .ifNotExist().create(); } } From 7f5a91738d910137fd2c91806073c00043c4483e Mon Sep 17 00:00:00 2001 From: contrueCT Date: Fri, 2 Oct 2026 13:08:37 +0800 Subject: [PATCH 4/4] fix(server): align index coverage with query visibility --- .../backend/tx/GraphIndexTransaction.java | 12 ++ .../apache/hugegraph/example/Example1.java | 4 +- .../core/SourceIndexCoverageTest.java | 152 ++++++++++++++++++ 3 files changed, 166 insertions(+), 2 deletions(-) diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java index 718917f39c..51e8ccebfb 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java @@ -806,6 +806,18 @@ private Set collectMatchedIndexes(ConditionQuery query, } } + if (requireCompleteCoverage) { + // Match the public result visibility, including flattened queries. + // Removal jobs drop indexes before data/schema; those labels must + // neither require coverage nor contribute unavailable index plans. + // Cleanup callers still need to inspect invisible labels' indexes. + Query visibility = query.rootOriginQuery(); + schemaLabels = schemaLabels.stream().filter(schemaLabel -> + (visibility.showHidden() || !schemaLabel.hidden()) && + (visibility.showDeleting() || !schemaLabel.status().deleting())) + .collect(Collectors.toList()); + } + // Collect MatchedIndex for each SchemaLabel Set matchedIndexes = InsertionOrderUtil.newSet(); for (SchemaLabel schemaLabel : schemaLabels) { diff --git a/hugegraph-server/hugegraph-example/src/main/java/org/apache/hugegraph/example/Example1.java b/hugegraph-server/hugegraph-example/src/main/java/org/apache/hugegraph/example/Example1.java index a6cda3932f..1d800ce796 100644 --- a/hugegraph-server/hugegraph-example/src/main/java/org/apache/hugegraph/example/Example1.java +++ b/hugegraph-server/hugegraph-example/src/main/java/org/apache/hugegraph/example/Example1.java @@ -479,10 +479,10 @@ public static void testLeftIndexProcess(final HugeGraph graph) { .has("city", "Hangzhou").toList(); assert vertices.isEmpty(); // set breakpoint here to see secondary_indexes and range_indexes table - vertices = graph.traversal().V().has("age", 28).toList(); + vertices = graph.traversal().V().hasLabel("person").has("age", 28).toList(); assert vertices.isEmpty(); // set breakpoint here to see secondary_indexes and range_indexes table - vertices = graph.traversal().V().has("city", "Hangzhou").toList(); + vertices = graph.traversal().V().hasLabel("person").has("city", "Hangzhou").toList(); assert vertices.isEmpty(); } } diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java index 98c9098ab7..06506d5b47 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java @@ -26,6 +26,7 @@ import org.apache.hugegraph.backend.query.ConditionQuery; import org.apache.hugegraph.exception.NoIndexException; import org.apache.hugegraph.schema.IndexLabel; +import org.apache.hugegraph.schema.SchemaLabel; import org.apache.hugegraph.schema.SchemaManager; import org.apache.hugegraph.testutil.Assert; import org.apache.hugegraph.tinkerpop.TestGraph; @@ -343,6 +344,139 @@ public void testIndexStatusAndRebuild() { graph().traversal().V().has("coverageName", "same").toSet()); } + @Test + public void testDeletingVertexLabelUsesQueryVisibility() { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + Set expected = this.addVertices(); + Vertex visible = expected.stream().filter(v -> v.label().equals("coverageA")) + .findFirst().get(); + SchemaLabel deleting = graph().schema().getVertexLabel("coverageB"); + try { + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.DELETING); + IndexLabel index = graph().schema().getIndexLabel("coverageBByName"); + this.params().schemaTransaction().updateSchemaStatus(index, SchemaStatus.DELETING); + // A hidden label's unavailable index must not be validated either. + Assert.assertEquals(ImmutableSet.of(visible), graph().traversal().V() + .has("coverageName", "same").toSet()); + this.params().schemaTransaction().updateSchemaStatus(index, SchemaStatus.CREATED); + ConditionQuery includeDeleting = this.nameQuery(HugeType.VERTEX); + includeDeleting.showDeleting(true); + Assert.assertEquals(expected, ImmutableSet.copyOf(graph().vertices(includeDeleting))); + graph().schema().indexLabel("coverageBByName").remove(); + + for (SchemaStatus status : new SchemaStatus[]{SchemaStatus.DELETING, + SchemaStatus.UNDELETED}) { + this.params().schemaTransaction().updateSchemaStatus(deleting, status); + Assert.assertEquals(ImmutableSet.of(visible), graph().traversal().V() + .has("coverageName", "same").toSet()); + ConditionQuery query = this.nameQuery(HugeType.VERTEX); + query.showDeleting(true); + Assert.assertThrows(NoIndexException.class, + () -> graph().vertices(query).hasNext()); + // Flattened queries inherit visibility from the outer request. + ConditionQuery child = query.copy(); + child.showDeleting(false); + child.setOriginQuery(query); + Assert.assertThrows(NoIndexException.class, + () -> graph().vertices(child).hasNext()); + } + } finally { + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.CREATED); + } + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().V() + .has("coverageName", "same").toSet()); + this.nameIndex("coverageB"); + Assert.assertEquals(expected, + graph().traversal().V().has("coverageName", "same").toSet()); + } + + @Test + public void testDeletingEdgeLabelUsesQueryVisibility() { + SchemaManager schema = graph().schema(); + for (String label : new String[]{"visibleEdge", "deletingEdge"}) { + schema.edgeLabel(label).link("coverageA", "coverageB") + .properties("coverageName").create(); + schema.indexLabel(label + "ByName").onE(label) + .by("coverageName").secondary().create(); + } + Set vertices = this.addVertices(); + Vertex a = vertices.stream().filter(v -> v.label().equals("coverageA")).findFirst().get(); + Vertex b = vertices.stream().filter(v -> v.label().equals("coverageB")).findFirst().get(); + Edge visible = a.addEdge("visibleEdge", b, "coverageName", "same"); + Edge removed = a.addEdge("deletingEdge", b, "coverageName", "same"); + this.commitTx(); + SchemaLabel deleting = schema.getEdgeLabel("deletingEdge"); + try { + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.DELETING); + ConditionQuery includeDeleting = this.nameQuery(HugeType.EDGE); + includeDeleting.showDeleting(true); + Assert.assertEquals(ImmutableSet.of(visible, removed), + ImmutableSet.copyOf(graph().edges(includeDeleting))); + schema.indexLabel("deletingEdgeByName").remove(); + for (SchemaStatus status : new SchemaStatus[]{SchemaStatus.DELETING, + SchemaStatus.UNDELETED}) { + this.params().schemaTransaction().updateSchemaStatus(deleting, status); + Assert.assertEquals(ImmutableSet.of(visible), graph().traversal().E() + .has("coverageName", "same").toSet()); + ConditionQuery query = this.nameQuery(HugeType.EDGE); + query.showDeleting(true); + Assert.assertThrows(NoIndexException.class, () -> graph().edges(query).hasNext()); + } + } finally { + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.CREATED); + } + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().E() + .has("coverageName", "same").toSet()); + schema.indexLabel("deletingEdgeByName").onE("deletingEdge") + .by("coverageName").secondary().create(); + Assert.assertEquals(ImmutableSet.of(visible, removed), graph().traversal().E() + .has("coverageName", "same").toSet()); + } + + @Test + public void testPagingWhileVertexLabelIsDeleting() { + Assume.assumeTrue(storeFeatures().supportsQueryByPage()); + this.nameIndex("coverageA"); + Set vertices = this.addVertices(); + Vertex a = vertices.stream().filter(v -> v.label().equals("coverageA")).findFirst().get(); + Vertex another = graph().addVertex(T.label, "coverageA", "coverageName", "same", + "coverageScore", 30, "coverageBody", "gold"); + this.commitTx(); + SchemaLabel deleting = graph().schema().getVertexLabel("coverageB"); + try { + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.DELETING); + Set actual = new HashSet<>(); + String page = ""; + int requests = 0; + do { + Iterator iterator = graph().vertices(this.pageQuery(page)); + try { + while (iterator.hasNext()) { + Assert.assertTrue(actual.add(iterator.next())); + } + page = PageInfo.pageInfo(iterator); + } finally { + CloseableIterator.closeIterator(iterator); + } + this.params().schemaTransaction().updateSchemaStatus(deleting, + SchemaStatus.UNDELETED); + Assert.assertTrue(++requests < 10); + } while (page != null && !page.isEmpty()); + Assert.assertEquals(ImmutableSet.of(a, another), actual); + Assert.assertTrue(requests > 1); + } finally { + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.CREATED); + } + Assert.assertThrows(NoIndexException.class, () -> this.page("")); + } + + private ConditionQuery nameQuery(HugeType type) { + ConditionQuery query = new ConditionQuery(type); + query.query(Condition.eq(graph().propertyKey("coverageName").id(), "same")); + return query; + } + @Test public void testGlobalEdgeCoverage() { SchemaManager schema = graph().schema(); @@ -398,6 +532,24 @@ public void testParentEdgeIndexCoversSubLabels() { schema.indexLabel("subBByName").onE("subB").by("coverageName").secondary().create(); Assert.assertEquals(2, graph().traversal().E().has("coverageName", "same").toList().size()); + + SchemaLabel deleting = schema.getEdgeLabel("subB"); + try { + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.DELETING); + schema.indexLabel("subBByName").remove(); + Assert.assertEquals(ImmutableList.of("subA"), graph().traversal().E() + .has("coverageName", "same").label().toList()); + ConditionQuery query = this.nameQuery(HugeType.EDGE); + query.showDeleting(true); + Assert.assertThrows(NoIndexException.class, () -> graph().edges(query).hasNext()); + } finally { + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.CREATED); + } + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().E() + .has("coverageName", "same").toList()); + schema.indexLabel("subBByName").onE("subB").by("coverageName").secondary().create(); + Assert.assertEquals(2, + graph().traversal().E().has("coverageName", "same").toList().size()); } @Test