diff --git a/docs/server-runtime.md b/docs/server-runtime.md index 5a27fb6b0c..da09a17c95 100644 --- a/docs/server-runtime.md +++ b/docs/server-runtime.md @@ -251,6 +251,43 @@ without a preceding REST graph query, and subsequent REST queries must remain us Gremlin scripts still cannot read restricted system properties such as `java.version` or disable the security manager. +### Property-query index coverage + +For type-compatible values, a global property-index query such as +`g.V().has("name", "alice")` or `g.E().has("weight", 0.5)` requires compatible +indexes on every visible candidate label. A candidate declares all queried non-OLAP +properties; even a label with no records requires coverage. Base edge labels have no +direct records; their indexes can cover sub-labels. An explicit sub-label query +also uses a compatible query-visible parent index when no compatible direct index +exists, while still filtering out sibling sub-labels. Shared OLAP indexes follow the graph read +mode. + +| Candidate state | Query behavior | +| --- | --- | +| Every visible candidate has compatible indexes | The query combines the indexed results. Composite and joint index plans may serve different labels in the same query. | +| A visible candidate lacks a compatible index and the values pass the property-type check | The query fails with `NoIndexException`, including when other candidates are hidden or deleting. | +| Every candidate is excluded by visibility | The query returns an empty result, including after those labels' indexes have been removed. | + +When query-visible candidates remain, index matching precedes the property-type +check. If at least one index matches but a value fails that check, the query returns +an empty result before complete coverage is checked. If no index matches, it still +raises `NoIndexException`, even for a type-mismatched value. + +Normal queries exclude hidden labels and labels in `DELETING` or `UNDELETED` +status. Internal requests that enable `showHidden` or `showDeleting` include the +corresponding labels and still require their indexes. Visibility follows the outer +request when a query is split into subqueries. Each branch checks index state and +complete coverage before opening its backend iterators. If branch construction +fails, the query closes cursors from both the unfinished branch and earlier +branches, preserving the original error. Joint index planning closes unselected +cursors and transfers the selected cursor to the result. This does not provide a +snapshot against concurrent schema changes. + +To fix incomplete coverage, specify an indexed label, for example +`g.V().hasLabel("person").has("name", "alice")`, or add indexes compatible with the +query predicates to every candidate label. Explicit-ID lookups and adjacency +filtering do not introduce a global property-index coverage requirement. + ### Predicate and value compatibility Version-specific handling retains negated predicates and filtering barriers where diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/page/IdHolder.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/page/IdHolder.java index e6fd34ed64..d5933cb0ff 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/page/IdHolder.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/page/IdHolder.java @@ -26,6 +26,7 @@ import org.apache.hugegraph.id.Id; import org.apache.hugegraph.query.ConditionQuery; import org.apache.hugegraph.query.Query; +import org.apache.hugegraph.backend.query.QueryBatch; import org.apache.hugegraph.backend.store.BackendEntry; import org.apache.hugegraph.iterator.CIter; import org.apache.hugegraph.iterator.Metadatable; @@ -231,16 +232,25 @@ public PageIds fetchNext(String page, long batchSize) { @Override public Set all() { + Set ids; try { - Set ids = this.fetcher.apply(this.remaining()); + ids = this.fetcher.apply(this.remaining()); if (this.currentBatch != null) { ids.addAll(this.getFromCurrentBatch(Query.NO_LIMIT).ids()); } this.count += ids.size(); - return ids; - } finally { - this.close(); + } catch (Throwable failure) { + try { + this.close(); + } catch (Throwable closing) { + if (closing != failure) { + failure.addSuppressed(closing); + } + } + throw QueryBatch.propagate(failure); } + this.close(); + return ids; } public PageIds peekNext(long size) { 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 8dc0b54d39..9935644324 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 @@ -43,6 +43,7 @@ import org.apache.hugegraph.backend.page.PageState; import org.apache.hugegraph.backend.page.SortByCountIdHolderList; import org.apache.hugegraph.backend.query.ConditionQueryFlatten; +import org.apache.hugegraph.backend.query.QueryBatch; import org.apache.hugegraph.backend.query.QueryResults; import org.apache.hugegraph.backend.serializer.AbstractSerializer; import org.apache.hugegraph.backend.store.BackendEntry; @@ -366,7 +367,31 @@ private IdHolderList queryByUserprop(ConditionQuery query) { query.conditionValues(HugeKeys.LABEL).isEmpty()) { return IdHolderList.empty(paging); } - Set indexes = this.collectMatchedIndexes(query); + List schemaLabels = this.querySchemaLabels(query); + // Use the outer request's visibility, including flattened queries. + Query visibility = query.rootOriginQuery(); + List visibleLabels = schemaLabels.stream().filter(label -> + (visibility.showHidden() || !label.hidden()) && + (visibility.showDeleting() || !label.status().deleting())) + .collect(Collectors.toList()); + if (visibleLabels.size() != schemaLabels.size()) { + Set properties = this.requiredIndexProperties(query); + boolean hadCandidates = schemaLabels.stream().anyMatch(label -> + isIndexQueryCandidate(label, properties)); + boolean hasVisibleCandidates = visibleLabels.stream().anyMatch(label -> + isIndexQueryCandidate(label, properties)); + // Only visibility exclusions justify an empty result here. Visible + // candidates without usable indexes must still fail below. + if (hadCandidates && !hasVisibleCandidates) { + IdHolderList empty = new IdHolderList(paging); + if (paging) { + // Keep a page slot so an empty first page is valid. + empty.add(new PagingIdHolder(query, q -> PageIds.EMPTY)); + } + return empty; + } + } + Set indexes = this.collectMatchedIndexes(query, visibleLabels); if (indexes.isEmpty()) { Id label = query.singleConditionValueOrNull(HugeKeys.LABEL); throw noIndexException(this.graph(), query, label); @@ -377,8 +402,10 @@ private IdHolderList queryByUserprop(ConditionQuery query) { return IdHolderList.empty(paging); } - // Do index query - IdHolderList holders = new IdHolderList(paging); + this.checkIndexCoverage(query, visibleLabels, indexes); + + // 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); @@ -386,28 +413,59 @@ private IdHolderList queryByUserprop(ConditionQuery query) { if (paging && index.indexLabels().size() > 1) { throw new NotSupportException("joint index query in paging"); } + } - if (index.containsSearchIndex()) { - // Do search-index query - holders.addAll(this.doSearchIndex(query, index)); - } else { - // Do secondary-index, range-index or shard-index query - IndexQueries queries = constructIndexQueries(query, index); - assert !paging || queries.size() <= 1; - IdHolder holder = this.doSingleOrJointIndex(queries); - holders.add(holder); + // Do index query + IdHolderList holders = new IdHolderList(paging); + try { + for (MatchedIndex index : indexes) { + ConditionQuery indexQuery = prepareIndexQuery(query, index); + if (index.containsSearchIndex()) { + // Do search-index query + holders.addAll(this.doSearchIndex(indexQuery, index)); + } else { + // Do secondary-index, range-index or shard-index query + IndexQueries queries = constructIndexQueries(indexQuery, index); + assert !paging || queries.size() <= 1; + IdHolder holder = this.doSingleOrJointIndex(queries); + holders.add(holder); + } + + /* + * NOTE: need to skip the offset if offset > 0, but can't handle + * it here because the query may a sub-query after flatten, + * so the offset will be handle in QueryList.IndexQuery + * + * TODO: finish early here if records exceeds required limit with + * FixedIdHolder. + */ } + return holders; + } catch (Throwable failure) { + throw closeIndexHolders(failure, holders); + } + } - /* - * NOTE: need to skip the offset if offset > 0, but can't handle - * it here because the query may a sub-query after flatten, - * so the offset will be handle in QueryList.IndexQuery - * - * TODO: finish early here if records exceeds required limit with - * FixedIdHolder. - */ + private static ConditionQuery prepareIndexQuery(ConditionQuery query, MatchedIndex index) { + Id label = query.singleConditionValueOrNull(HugeKeys.LABEL); + if (!query.resultType().isEdge() || label == null || + index.schemaLabel().id().equals(label)) { + return query; + } + // BaseEdge exposes the parent as LABEL and the actual child as SUB_LABEL. + // Filter on the child after fetching from the shared parent index. + ConditionQuery filtered = query.copy(); + filtered.unsetCondition(HugeKeys.LABEL); + filtered.eq(HugeKeys.SUB_LABEL, label); + filtered.optimized(OptimizedType.INDEX_FILTER); + if (!query.paging()) { + filtered.limit(Query.NO_LIMIT); } - return holders; + if (!index.containsSearchIndex()) { + // Search queries install their word-aware filter after rewriting. + filtered.registerResultsFilter(element -> filtered.test(element, null)); + } + return filtered; } @Watched(prefix = "index") @@ -417,18 +475,22 @@ private IdHolderList doSearchIndex(ConditionQuery query, // Sorted by matched count IdHolderList holders = new SortByCountIdHolderList(query.paging()); List flatten = ConditionQueryFlatten.flatten(query); - for (ConditionQuery q : flatten) { - if (!q.noLimit() && flatten.size() > 1) { - // Increase limit for union operation - increaseLimit(q); + try { + for (ConditionQuery q : flatten) { + if (!q.noLimit() && flatten.size() > 1) { + // Increase limit for union operation + increaseLimit(q); + } + IndexQueries queries = constructIndexQueries(q, index); + assert !query.paging() || queries.size() <= 1; + IdHolder holder = this.doSingleOrJointIndex(queries); + // NOTE: ids will be merged into one IdHolder if not in paging + holders.add(holder); } - IndexQueries queries = constructIndexQueries(q, index); - assert !query.paging() || queries.size() <= 1; - IdHolder holder = this.doSingleOrJointIndex(queries); - // NOTE: ids will be merged into one IdHolder if not in paging - holders.add(holder); + return holders; + } catch (Throwable failure) { + throw closeIndexHolders(failure, holders); } - return holders; } @Watched(prefix = "index") @@ -460,61 +522,79 @@ private IdHolder doJointIndex(IndexQueries queries) { Set intersectIds = null; boolean filtering = false; IdHolder resultHolder = null; - for (Map.Entry e : queries.entrySet()) { - IndexLabel indexLabel = e.getKey(); - ConditionQuery query = e.getValue(); - assert !query.paging(); - if (!query.noLimit() && queries.size() > 1) { - // Unset limit for intersection operation - query.limit(Query.NO_LIMIT); - } - /* - * Try to query by joint indexes: - * 1 If there is any index exceeded the threshold, transform into - * partial index query, then filter after back-table. - * 1.1 Return the holder of the first index that not exceeded the - * threshold if there exists one index, this holder will be used - * as the only query condition. - * 1.2 Return the holder of the first index if all indexes exceeded - * the threshold. - * 2 Else intersect holders for all indexes, and return intersection - * ids of all indexes. - */ - IdHolder holder = this.doIndexQuery(indexLabel, query); - if (resultHolder == null) { - resultHolder = holder; - this.storeSelectedIndexField(indexLabel, query); - } - assert this.indexIntersectThresh > 0; // default value is 1000 - Set ids = ((BatchIdHolder) holder).peekNext( - this.indexIntersectThresh).ids(); - if (ids.size() >= this.indexIntersectThresh) { - // Transform into filtering - filtering = true; - query.optimized(OptimizedType.INDEX_FILTER); - } else if (filtering) { - assert ids.size() < this.indexIntersectThresh; - resultHolder = holder; - this.storeSelectedIndexField(indexLabel, query); - break; - } else { - if (intersectIds == null) { - intersectIds = ids; - } else { - CollectionUtil.intersectWithModify(intersectIds, ids); + List holders = new ArrayList<>(queries.size()); + try { + for (Map.Entry e : queries.entrySet()) { + IndexLabel indexLabel = e.getKey(); + ConditionQuery query = e.getValue(); + assert !query.paging(); + if (!query.noLimit() && queries.size() > 1) { + // Unset limit for intersection operation + query.limit(Query.NO_LIMIT); + } + /* + * Try to query by joint indexes: + * 1 If there is any index exceeded the threshold, transform into + * partial index query, then filter after back-table. + * 1.1 Return the holder of the first index that not exceeded the + * threshold if there exists one index, this holder will be used + * as the only query condition. + * 1.2 Return the holder of the first index if all indexes exceeded + * the threshold. + * 2 Else intersect holders for all indexes, and return intersection + * ids of all indexes. + */ + IdHolder holder = this.doIndexQuery(indexLabel, query); + holders.add(holder); + if (resultHolder == null) { + resultHolder = holder; + this.storeSelectedIndexField(indexLabel, query); } - if (intersectIds.isEmpty()) { + assert this.indexIntersectThresh > 0; // default value is 1000 + Set ids = ((BatchIdHolder) holder).peekNext( + this.indexIntersectThresh).ids(); + if (ids.size() >= this.indexIntersectThresh) { + // Transform into filtering + filtering = true; + query.optimized(OptimizedType.INDEX_FILTER); + } else if (filtering) { + assert ids.size() < this.indexIntersectThresh; + resultHolder = holder; + this.storeSelectedIndexField(indexLabel, query); break; + } else { + if (intersectIds == null) { + intersectIds = ids; + } else { + CollectionUtil.intersectWithModify(intersectIds, ids); + } + if (intersectIds.isEmpty()) { + break; + } } } + + IdHolder result = filtering ? resultHolder : + new FixedIdHolder(queries.asJointQuery(), intersectIds); + // Transfer only the selected holder. Other lazy cursors may still + // be open when an index exceeded the intersection threshold. + QueryBatch.closeAll(holders.stream().filter(holder -> holder != result).toArray()); + return result; + } catch (Throwable failure) { + throw closeIndexHolders(failure, holders); } + } - if (filtering) { - return resultHolder; - } else { - assert intersectIds != null; - return new FixedIdHolder(queries.asJointQuery(), intersectIds); + private static RuntimeException closeIndexHolders(Throwable failure, + Collection holders) { + try { + QueryBatch.closeAll(holders.toArray()); + } catch (Throwable closing) { + if (closing != failure) { + failure.addSuppressed(closing); + } } + return QueryBatch.propagate(failure); } private void storeSelectedIndexField(IndexLabel indexLabel, @@ -650,15 +730,14 @@ private PageIds doIndexQueryOnce(IndexLabel indexLabel, } } - @Watched(prefix = "index") - private Set collectMatchedIndexes(ConditionQuery query) { + private List querySchemaLabels(ConditionQuery query) { ISchemaTransaction schema = this.params().schemaTransaction(); Set labels = query.conditionValues(HugeKeys.LABEL); List schemaLabels; if (query.containsConditionValues(HugeKeys.LABEL) && labels.isEmpty()) { // An empty intersection is not an unrestricted label query. - return Collections.emptySet(); + return Collections.emptyList(); } if (labels.size() == 1) { Id label = (Id) labels.iterator().next(); @@ -674,6 +753,12 @@ private Set collectMatchedIndexes(ConditionQuery query) { query.resultType())); } schemaLabels = ImmutableList.of(schemaLabel); + if (schemaLabel instanceof EdgeLabel && ((EdgeLabel) schemaLabel).hasFather()) { + // Include the parent in visibility filtering and index matching; + // the original query label still filters out sibling edges. + EdgeLabel father = schema.getEdgeLabel(((EdgeLabel) schemaLabel).fatherId()); + schemaLabels = ImmutableList.of(schemaLabel, father); + } } else { // Query doesn't have LABEL condition or it doesn't resolve // to a single label, so keep the conservative fallback. @@ -688,17 +773,86 @@ private Set collectMatchedIndexes(ConditionQuery query) { } } + return schemaLabels; + } + + private Set collectMatchedIndexes(ConditionQuery query) { + // Cleanup callers must still inspect invisible labels' indexes. + return this.collectMatchedIndexes(query, this.querySchemaLabels(query)); + } + + @Watched(prefix = "index") + private Set collectMatchedIndexes(ConditionQuery query, + List schemaLabels) { // Collect MatchedIndex for each SchemaLabel Set matchedIndexes = InsertionOrderUtil.newSet(); for (SchemaLabel schemaLabel : schemaLabels) { MatchedIndex index = this.collectMatchedIndex(schemaLabel, query); if (index != null) { matchedIndexes.add(index); + // Prefer a compatible direct index for an explicit sub-label. + // Its parent, when present, follows it in querySchemaLabels(). + if (schemaLabel.id().equals(query.singleConditionValueOrNull(HugeKeys.LABEL))) { + break; + } } } return matchedIndexes; } + private static boolean isIndexQueryCandidate(SchemaLabel label, Set properties) { + // Base edge labels describe sub-labels and have no direct records. + return label.properties().containsAll(properties) && + !(label instanceof EdgeLabel && ((EdgeLabel) label).isFather()); + } + + private Set requiredIndexProperties(ConditionQuery query) { + 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()); + } + return requiredProperties; + } + + 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 = this.requiredIndexProperties(query); + 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()); + } + 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-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java index b0093b924c..3b55a141bb 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java @@ -1523,23 +1523,35 @@ private QueryList optimizeQueries(Query query, return queries; } - boolean supportIn = this.storeFeatures().supportsQueryWithInCondition(); - for (ConditionQuery cq : ConditionQueryFlatten.flatten( - (ConditionQuery) query, supportIn)) { - // Optimize by sysprop - Query q = this.optimizeQuery(cq); - /* - * NOTE: There are two possibilities for the returned q: - * 1.sysprop-query, which would not be empty. - * 2.index-query result(ids after optimization), which may be empty. - */ - if (q == null) { - queries.add(this.indexQuery(cq), this.batchSize); - } else if (!q.empty()) { - queries.add(q); + try { + boolean supportIn = this.storeFeatures().supportsQueryWithInCondition(); + for (ConditionQuery cq : ConditionQueryFlatten.flatten( + (ConditionQuery) query, supportIn)) { + // Optimize by sysprop + Query q = this.optimizeQuery(cq); + /* + * NOTE: There are two possibilities for the returned q: + * 1.sysprop-query, which would not be empty. + * 2.index-query result(ids after optimization), which may be empty. + */ + if (q == null) { + queries.add(this.indexQuery(cq), this.batchSize); + } else if (!q.empty()) { + queries.add(q); + } + } + return queries; + } catch (Throwable failure) { + // Earlier flattened branches may already own backend cursors. + try { + queries.close(); + } catch (Throwable closing) { + if (closing != failure) { + failure.addSuppressed(closing); + } } + throw QueryBatch.propagate(failure); } - return queries; } private Query optimizeQuery(ConditionQuery query) { 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 03c4a0a643..b65b860162 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/CoreTestSuite.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java index 4f3ea5436c..6eb8b29733 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 @@ -45,6 +45,7 @@ EdgeLabelCoreTest.class, IndexLabelCoreTest.class, VertexCoreTest.class, + SourceIndexCoverageTest.class, EdgeCoreTest.class, CountStrategyCoreTest.class, IdPredicateCoreTest.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 52bd058c6b..aa414df0ae 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 @@ -797,7 +797,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()); @@ -809,7 +809,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()); } @@ -839,27 +839,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)); @@ -871,22 +871,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()); } @@ -920,31 +920,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)); @@ -956,26 +956,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()); } @@ -993,7 +993,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()); @@ -1005,7 +1005,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()); } @@ -1052,7 +1052,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()); @@ -1070,10 +1070,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(); @@ -1085,9 +1085,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()); } @@ -1117,27 +1117,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)); @@ -1165,29 +1165,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)); @@ -1200,25 +1200,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()); } @@ -1252,31 +1252,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)); @@ -1307,38 +1307,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)); @@ -1351,33 +1351,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()); } @@ -1395,7 +1395,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()); @@ -1412,10 +1412,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(); @@ -1427,7 +1427,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()); } @@ -1487,7 +1487,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)); @@ -1500,7 +1500,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()); } @@ -1545,27 +1545,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)); @@ -1578,22 +1578,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()); } @@ -1645,31 +1645,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)); @@ -1682,26 +1682,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()); } @@ -1724,7 +1724,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)); @@ -1737,7 +1737,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()); } @@ -5028,10 +5028,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()); } @@ -5057,36 +5057,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()); } @@ -5106,17 +5106,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); @@ -7380,10 +7380,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()); } @@ -7402,28 +7402,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")); } @@ -7474,18 +7474,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")); } @@ -7504,11 +7504,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..f132091f1c --- /dev/null +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/SourceIndexCoverageTest.java @@ -0,0 +1,1240 @@ +/* + * 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.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.hugegraph.backend.page.PageInfo; +import org.apache.hugegraph.backend.serializer.AbstractSerializer; +import org.apache.hugegraph.backend.store.BackendEntry; +import org.apache.hugegraph.backend.store.BackendStore; +import org.apache.hugegraph.backend.tx.GraphIndexTransaction; +import org.apache.hugegraph.exception.NoIndexException; +import org.apache.hugegraph.query.Condition; +import org.apache.hugegraph.query.ConditionQuery; +import org.apache.hugegraph.query.Query; +import org.apache.hugegraph.schema.SchemaManager; +import org.apache.hugegraph.struct.schema.IndexLabel; +import org.apache.hugegraph.struct.schema.SchemaLabel; +import org.apache.hugegraph.struct.schema.VertexLabel; +import org.apache.hugegraph.testutil.Assert; +import org.apache.hugegraph.testutil.Whitebox; +import org.apache.hugegraph.tinkerpop.TestGraph; +import org.apache.hugegraph.traversal.optimize.HugeGraphStep; +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.HugeKeys; +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; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import org.mockito.AdditionalAnswers; +import org.mockito.Mockito; + +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +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 testCompleteCandidatesReturnAllResults() { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + Set expected = this.addVertices(); + 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 + 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 testOrCoverageFailureClosesEarlierBranchCursors() { + this.prepareOrIndexes(false); + this.addVertices(); + try (IndexCursorTracker tracker = new IndexCursorTracker(null)) { + NoIndexException failure = Assert.assertThrows(NoIndexException.class, + () -> graph().vertices(this.orQuery())); + Assert.assertContains("Incomplete index coverage", failure.getMessage()); + tracker.assertCounts(2, 1); + Assert.assertEquals(0, failure.getSuppressed().length); + // Closing must happen before rollback or test-fixture cleanup. + graph().tx().rollback(); + tracker.assertCounts(2, 1); + } + } + + @Test + public void testOrCoverageFailurePreservesOriginalErrorWhenCloseFails() { + this.prepareOrIndexes(false); + this.addVertices(); + RuntimeException closeFailure = new IllegalStateException("index cursor close failed"); + try (IndexCursorTracker tracker = new IndexCursorTracker(closeFailure)) { + NoIndexException failure = Assert.assertThrows(NoIndexException.class, + () -> graph().vertices(this.orQuery())); + Assert.assertContains("Incomplete index coverage", failure.getMessage()); + // A failed close on the first cursor must not skip the second one. + tracker.assertCounts(2, 1); + Assert.assertEquals(1, failure.getSuppressed().length); + Assert.assertSame(closeFailure, Throwables.getRootCause(failure.getSuppressed()[0])); + } + } + + @Test + public void testCompleteOrPlanKeepsCursorsUntilResultClose() { + this.prepareOrIndexes(true); + Set expected = this.addVertices(); + try (IndexCursorTracker tracker = new IndexCursorTracker(null)) { + Iterator results = graph().vertices(this.orQuery()); + try { + tracker.assertCounts(4, 0); + Assert.assertTrue(results.hasNext()); + Assert.assertTrue(expected.contains(results.next())); + } finally { + CloseableIterator.closeIterator(results); + } + // Includes the later OR branch whose results were never requested. + tracker.assertCounts(4, 1); + } + } + + @Test + public void testLabeledSubEdgeUsesParentIndex() { + Assume.assumeTrue(storeFeatures().supportsFatherAndSubEdgeLabel()); + SchemaManager schema = graph().schema(); + List edges = this.addSubEdges(); + Edge expected = edges.get(0); + Edge sibling = edges.get(1); + schema.indexLabel("parentEdgeByName").onE("parentEdge") + .by("coverageName").secondary().create(); + Assert.assertEquals(2, + graph().traversal().E().has("coverageName", "same").toList().size()); + Assert.assertEquals(ImmutableList.of(sibling), graph().traversal().E() + .hasLabel("subB").has("coverageName", "same").limit(1).toList()); + Assert.assertEquals(ImmutableSet.of(expected), graph().traversal().E() + .hasLabel("subA").has("coverageName", "same").toSet()); + Assert.assertEquals(ImmutableList.of(expected), graph().traversal().E() + .hasLabel("subA").has("coverageName", "same").limit(1).toList()); + if (storeFeatures().supportsQueryByPage()) { + for (Edge edge : edges) { + Set actual = new HashSet<>(); + String page = ""; + int pages = 0; + do { + ConditionQuery query = this.nameQuery(HugeType.EDGE); + query.eq(HugeKeys.LABEL, graph().edgeLabel(edge.label()).id()); + query.limit(1); + query.page(page); + Iterator results = graph().edges(query); + try { + results.forEachRemaining(actual::add); + page = PageInfo.pageInfo(results); + } finally { + CloseableIterator.closeIterator(results); + } + Assert.assertTrue(++pages < 10); + } while (page != null && !page.isEmpty()); + Assert.assertEquals(ImmutableSet.of(edge), actual); + } + } + + SchemaLabel parentLabel = schema.getEdgeLabel("parentEdge"); + try { + this.params().schemaTransaction().updateSchemaStatus(parentLabel, SchemaStatus.DELETING); + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().E() + .hasLabel("subA").has("coverageName", "same").toSet()); + ConditionQuery query = this.nameQuery(HugeType.EDGE); + query.eq(HugeKeys.LABEL, graph().edgeLabel("subA").id()); + query.showDeleting(true); + Assert.assertEquals(ImmutableSet.of(expected), ImmutableSet.copyOf(graph().edges(query))); + } finally { + this.params().schemaTransaction().updateSchemaStatus(parentLabel, SchemaStatus.CREATED); + } + + IndexLabel parent = schema.getIndexLabel("parentEdgeByName"); + try { + this.params().schemaTransaction().updateSchemaStatus(parent, SchemaStatus.REBUILDING); + try (IndexCursorTracker tracker = new IndexCursorTracker(null)) { + Assert.assertThrows(IllegalArgumentException.class, () -> graph().traversal().E() + .hasLabel("subA").has("coverageName", "same").toSet(), error -> + Assert.assertContains("REBUILDING", error.getMessage())); + tracker.assertCounts(0, 0); + } + schema.indexLabel("subAByName").onE("subA").by("coverageName").secondary().create(); + // A compatible direct index is preferred over the parent fallback. + try (IndexCursorTracker tracker = new IndexCursorTracker(null)) { + Assert.assertEquals(ImmutableSet.of(expected), graph().traversal().E() + .hasLabel("subA").has("coverageName", "same").toSet()); + tracker.assertCounts(1, 1); + } + } finally { + this.params().schemaTransaction().updateSchemaStatus(parent, SchemaStatus.CREATED); + } + } + + @Test + public void testLabeledSubEdgeUsesParentRangeJointAndSearchIndexes() { + Assume.assumeTrue(storeFeatures().supportsFatherAndSubEdgeLabel()); + SchemaManager schema = graph().schema(); + Edge expected = this.addSubEdges().get(1); + schema.indexLabel("parentByScore").onE("parentEdge").by("coverageScore").range().create(); + Assert.assertEquals(ImmutableList.of(expected), graph().traversal().E() + .hasLabel("subB").has("coverageScore", P.gte(1)).limit(1).toList()); + schema.indexLabel("parentByName").onE("parentEdge").by("coverageName").secondary().create(); + Assert.assertEquals(ImmutableList.of(expected), graph().traversal().E() + .hasLabel("subB").has("coverageName", "same") + .has("coverageScore", P.gte(1)).limit(1).toList()); + schema.indexLabel("parentByName").remove(); + schema.indexLabel("parentSearchByName").onE("parentEdge").by("coverageName").search().create(); + Assert.assertEquals(ImmutableList.of(expected), graph().traversal().E() + .hasLabel("subB").has("coverageName", Text.contains("same")).limit(1).toList()); + } + + private List addSubEdges() { + 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(); + } + 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 first = a.addEdge("subA", b, "coverageName", "same", "coverageScore", 1); + Edge second = a.addEdge("subB", b, "coverageName", "same", "coverageScore", 2); + this.commitTx(); + return ImmutableList.of(first, second); + } + + @Test + public void testSingleBranchFailureClosesPartialCursors() { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + this.addVertices(); + RuntimeException injected = new IllegalStateException("second index query failed"); + try (IndexCursorTracker tracker = new IndexCursorTracker(null, injected, 2)) { + RuntimeException failure = Assert.assertThrows(RuntimeException.class, + () -> graph().vertices(this.nameQuery(HugeType.VERTEX))); + Assert.assertSame(injected, Throwables.getRootCause(failure)); + tracker.assertCounts(1, 1); + } + } + + @Test + public void testJointBranchFailureClosesPartialCursors() { + this.prepareJointIndexes(); + ConditionQuery query = this.jointQuery(); + RuntimeException injected = new IllegalStateException("second joint index query failed"); + try (IndexCursorTracker tracker = new IndexCursorTracker(null, injected, 2)) { + RuntimeException failure = Assert.assertThrows(RuntimeException.class, + () -> graph().vertices(query)); + Assert.assertSame(injected, Throwables.getRootCause(failure)); + tracker.assertCounts(1, 1); + } + } + + @Test + public void testSingleBranchFailurePreservesErrorAndClosesAllCursors() { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + graph().schema().vertexLabel("coverageC").properties("coverageName") + .useAutomaticId().create(); + this.nameIndex("coverageC"); + this.addVertices(); + RuntimeException injected = new IllegalStateException("third index query failed"); + RuntimeException closing = new IllegalStateException("first index cursor close failed"); + try (IndexCursorTracker tracker = new IndexCursorTracker(closing, injected, 3)) { + RuntimeException failure = Assert.assertThrows(RuntimeException.class, + () -> graph().vertices(this.nameQuery(HugeType.VERTEX))); + Assert.assertSame(injected, Throwables.getRootCause(failure)); + tracker.assertCounts(2, 1); + Assert.assertEquals(1, injected.getSuppressed().length); + Assert.assertSame(closing, Throwables.getRootCause(injected.getSuppressed()[0])); + } + } + + @Test + public void testJointPlanClosesUnselectedCursorsAndTransfersSelected() { + Set expected = this.prepareJointIndexes(); + ConditionQuery query = this.jointQuery(); + try (IndexCursorTracker tracker = new IndexCursorTracker(null)) { + Iterator results = graph().vertices(query); + try { + Assert.assertEquals(2, tracker.cursors.size()); + Assert.assertEquals(1, tracker.cursors.stream().mapToInt(cursor -> cursor.closes).sum()); + Assert.assertEquals(expected, ImmutableSet.copyOf(results)); + } finally { + CloseableIterator.closeIterator(results); + } + tracker.assertCounts(2, 1); + } + } + + @Test + public void testJointReadFailureClosesAllCursorsAndPreservesError() { + this.prepareJointIndexes(); + RuntimeException injected = new IllegalStateException("second joint cursor read failed"); + RuntimeException closing = new IllegalStateException("first joint cursor close failed"); + try (IndexCursorTracker tracker = new IndexCursorTracker(closing)) { + tracker.failOnRead = 2; + tracker.readFailure = injected; + RuntimeException failure = Assert.assertThrows(RuntimeException.class, + () -> graph().vertices(this.jointQuery())); + Assert.assertSame(injected, Throwables.getRootCause(failure)); + tracker.assertCounts(2, 1); + Assert.assertEquals(1, injected.getSuppressed().length); + Assert.assertSame(closing, Throwables.getRootCause(injected.getSuppressed()[0])); + } + } + + @Test + public void testSearchReadFailureClosesCursorAndPreservesError() { + graph().schema().indexLabel("coverageASearch").onV("coverageA") + .by("coverageBody").search().create(); + this.addVertices(); + RuntimeException injected = new IllegalStateException("search index cursor read failed"); + RuntimeException closing = new IllegalStateException("search index cursor close failed"); + try (IndexCursorTracker tracker = new IndexCursorTracker(closing)) { + tracker.failOnRead = 1; + tracker.readFailure = injected; + RuntimeException failure = Assert.assertThrows(RuntimeException.class, + () -> graph().traversal().V().hasLabel("coverageA") + .has("coverageBody", Text.contains("gold")).toSet()); + Assert.assertSame(injected, Throwables.getRootCause(failure)); + tracker.assertCounts(1, 1); + Assert.assertEquals(1, injected.getSuppressed().length); + Assert.assertSame(closing, Throwables.getRootCause(injected.getSuppressed()[0])); + } + } + + @Test + public void testSearchDecodeFailureClosesCursorAndPreservesError() { + graph().schema().indexLabel("coverageASearch").onV("coverageA") + .by("coverageBody").search().create(); + this.addVertices(); + RuntimeException injected = new IllegalStateException("search index decode failed"); + RuntimeException closing = new IllegalStateException("search index cursor close failed"); + GraphIndexTransaction indexTx = Whitebox.getInternalState(params().graphTransaction(), "indexTx"); + AbstractSerializer original = Whitebox.getInternalState(indexTx, "serializer"); + AbstractSerializer observed = Mockito.mock(AbstractSerializer.class, + AdditionalAnswers.delegatesTo(original)); + Mockito.doAnswer(invocation -> { + // Decode a real backend entry before failing outside the iterator wrapper. + original.readIndex(invocation.getArgument(0), invocation.getArgument(1), + invocation.getArgument(2)); + throw injected; + }).when(observed).readIndex(Mockito.any(), Mockito.any(), Mockito.any()); + Whitebox.setInternalState(indexTx, "serializer", observed); + try (IndexCursorTracker tracker = new IndexCursorTracker(closing)) { + RuntimeException failure = Assert.assertThrows(RuntimeException.class, + () -> graph().traversal().V().hasLabel("coverageA") + .has("coverageBody", Text.contains("gold")).toSet()); + Assert.assertSame(injected, Throwables.getRootCause(failure)); + tracker.assertCounts(1, 1); + Assert.assertEquals(1, injected.getSuppressed().length); + Assert.assertSame(closing, Throwables.getRootCause(injected.getSuppressed()[0])); + } finally { + Whitebox.setInternalState(indexTx, "serializer", original); + } + } + + private Set prepareJointIndexes() { + this.nameIndex("coverageA"); + graph().schema().indexLabel("coverageAByScore").onV("coverageA") + .by("coverageScore").secondary().create(); + // Exceed the fixture's intersection threshold of 2 to retain lazy cursors. + Set expected = new HashSet<>(); + for (int i = 0; i < 3; i++) { + expected.add(graph().addVertex(T.label, "coverageA", "coverageName", "same", + "coverageScore", 10, "coverageBody", "gold")); + } + this.commitTx(); + return expected; + } + + private ConditionQuery jointQuery() { + ConditionQuery query = this.nameQuery(HugeType.VERTEX); + query.eq(HugeKeys.LABEL, graph().vertexLabel("coverageA").id()); + query.query(Condition.eq(graph().propertyKey("coverageScore").id(), 10)); + return query; + } + + private void prepareOrIndexes(boolean complete) { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + graph().schema().indexLabel("coverageAByScore").onV("coverageA") + .by("coverageScore").secondary().create(); + if (complete) { + graph().schema().indexLabel("coverageBByScore").onV("coverageB") + .by("coverageScore").secondary().create(); + } + } + + private ConditionQuery orQuery() { + ConditionQuery query = new ConditionQuery(HugeType.VERTEX); + query.query(Condition.or( + Condition.eq(graph().propertyKey("coverageName").id(), "same"), + Condition.eq(graph().propertyKey("coverageScore").id(), 10))); + return query; + } + + private final class IndexCursorTracker implements AutoCloseable { + + private final GraphIndexTransaction indexTx; + private final BackendStore original; + private final List cursors = new ArrayList<>(); + private int failOnRead; + private RuntimeException readFailure; + + private IndexCursorTracker(RuntimeException closeFailure) { + this(closeFailure, null, 0); + } + + private IndexCursorTracker(RuntimeException closeFailure, RuntimeException queryFailure, + int failOnQuery) { + this.indexTx = Whitebox.getInternalState(params().graphTransaction(), "indexTx"); + this.original = Whitebox.getInternalState(this.indexTx, "store"); + BackendStore observed = Mockito.mock(BackendStore.class, + AdditionalAnswers.delegatesTo(this.original)); + Mockito.doAnswer(invocation -> { + if (queryFailure != null && this.cursors.size() + 1 == failOnQuery) { + throw queryFailure; + } + // Keep real index matching, validation, serialization and backend queries. + Iterator entries = this.original.query((Query) invocation.getArgument(0)); + CountingIndexCursor cursor = new CountingIndexCursor(entries, + this.cursors.isEmpty() ? closeFailure : null); + this.cursors.add(cursor); + if (this.cursors.size() == this.failOnRead) { + cursor.readFailure = this.readFailure; + } + return cursor; + }).when(observed).query(Mockito.any(Query.class)); + Whitebox.setInternalState(this.indexTx, "store", observed); + } + + private void assertCounts(int opened, int closesPerCursor) { + Assert.assertEquals(opened, this.cursors.size()); + for (CountingIndexCursor cursor : this.cursors) { + Assert.assertEquals(closesPerCursor, cursor.closes); + } + } + + @Override + public void close() { + Whitebox.setInternalState(this.indexTx, "store", this.original); + // Release leaks even when the regression assertion fails on old code. + for (CountingIndexCursor cursor : this.cursors) { + if (cursor.closes == 0) { + CloseableIterator.closeIterator(cursor.entries); + } + } + } + } + + private static final class CountingIndexCursor implements CloseableIterator { + + private final Iterator entries; + private final RuntimeException closeFailure; + private int closes; + private RuntimeException readFailure; + + private CountingIndexCursor(Iterator entries, RuntimeException closeFailure) { + this.entries = entries; + this.closeFailure = closeFailure; + } + + @Override + public boolean hasNext() { + if (this.readFailure != null) { + throw this.readFailure; + } + return this.entries.hasNext(); + } + + @Override + public BackendEntry next() { + return this.entries.next(); + } + + @Override + public void close() { + this.closes++; + CloseableIterator.closeIterator(this.entries); + if (this.closeFailure != null) { + throw this.closeFailure; + } + } + } + + @Test + public void testNegativeRootLabelKeepsPropertyFilterLocal() { + SchemaManager schema = graph().schema(); + schema.propertyKey("name").asText().create(); + for (String label : new String[]{"indexed", "unindexed"}) { + schema.vertexLabel(label).properties("name").useAutomaticId().create(); + } + schema.indexLabel("indexedByName").onV("indexed").by("name").secondary().create(); + Vertex allowed = graph().addVertex(T.label, "indexed", "name", "alice"); + graph().addVertex(T.label, "unindexed", "name", "alice"); + graph().addVertex(T.label, "indexed", "name", "bob"); + this.commitTx(); + + // Control: the unqualified indexed query really has incomplete coverage. + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().V() + .has("name", "alice").toSet(), error -> + Assert.assertContains("Incomplete index coverage", error.getMessage())); + + try (IndexCursorTracker tracker = new IndexCursorTracker(null)) { + GraphTraversal query = graph().traversal().V() + .hasLabel(P.neq("unindexed")).has("name", "alice"); + query.asAdmin().applyStrategies(); + HugeGraphStep source = (HugeGraphStep) query.asAdmin().getStartStep(); + // The negative label keeps both predicates local, so the source + // scans vertices without running property-index coverage checks. + Assert.assertTrue(source.getHasContainers().isEmpty()); + Assert.assertEquals(ImmutableSet.of(allowed), query.toSet()); + tracker.assertCounts(0, 0); + } + } + + @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"); + 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 testUnlabeledMixedCompositeAndJointIndexes() { + SchemaManager schema = graph().schema(); + schema.indexLabel("coverageAComposite").onV("coverageA") + .by("coverageName", "coverageScore").secondary().create(); + this.nameIndex("coverageB"); + schema.indexLabel("coverageBRange").onV("coverageB") + .by("coverageScore").range().create(); + Set expected = this.addVertices(); + for (Vertex vertex : expected) { + vertex.property("coverageScore", 10); + } + // Each individual index also matches a vertex outside the intersection. + graph().addVertex(T.label, "coverageB", "coverageName", "other", + "coverageScore", 10, "coverageBody", "gold"); + graph().addVertex(T.label, "coverageB", "coverageName", "same", + "coverageScore", 30, "coverageBody", "gold"); + this.commitTx(); + Assert.assertEquals(expected, graph().traversal().V() + .has("coverageName", "same").has("coverageScore", 10).toSet()); + + IndexLabel index = schema.getIndexLabel("coverageBRange"); + try { + this.params().schemaTransaction().updateSchemaStatus(index, SchemaStatus.REBUILDING); + Assert.assertThrows(IllegalArgumentException.class, () -> graph().traversal().V() + .has("coverageName", "same").has("coverageScore", 10).toSet()); + } finally { + this.params().schemaTransaction().updateSchemaStatus(index, SchemaStatus.CREATED); + } + Assert.assertEquals(expected, graph().traversal().V() + .has("coverageName", "same").has("coverageScore", 10).toSet()); + } + + @Test + public void testAllVertexCandidatesDeletingReturnEmpty() { + this.nameIndex("coverageA"); + this.nameIndex("coverageB"); + Set expected = this.addVertices(); + SchemaLabel a = graph().schema().getVertexLabel("coverageA"); + SchemaLabel b = graph().schema().getVertexLabel("coverageB"); + try { + for (SchemaStatus status : new SchemaStatus[]{SchemaStatus.DELETING, + SchemaStatus.UNDELETED}) { + this.params().schemaTransaction().updateSchemaStatus(a, status); + this.params().schemaTransaction().updateSchemaStatus(b, status); + Assert.assertEquals(ImmutableSet.of(), graph().traversal().V() + .has("coverageName", "same").toSet()); + Assert.assertEquals(ImmutableSet.of(), graph().traversal().V() + .hasLabel("coverageA").has("coverageName", "same").toSet()); + this.assertEmptyPage(this.nameQuery(HugeType.VERTEX)); + ConditionQuery query = this.nameQuery(HugeType.VERTEX); + query.showDeleting(true); + Assert.assertEquals(expected, ImmutableSet.copyOf(graph().vertices(query))); + ConditionQuery child = query.copy(); + child.showDeleting(false); + child.setOriginQuery(query); + Assert.assertEquals(expected, ImmutableSet.copyOf(graph().vertices(child))); + } + graph().schema().indexLabel("coverageAByName").remove(); + graph().schema().indexLabel("coverageBByName").remove(); + Assert.assertEquals(ImmutableSet.of(), graph().traversal().V() + .has("coverageName", "same").toSet()); + ConditionQuery query = this.nameQuery(HugeType.VERTEX); + query.showDeleting(true); + Assert.assertThrows(NoIndexException.class, () -> graph().vertices(query).hasNext()); + // A visible, unindexed candidate must still fail next to an invisible one. + this.params().schemaTransaction().updateSchemaStatus(a, SchemaStatus.CREATED); + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().V() + .has("coverageName", "same").toSet()); + } finally { + this.params().schemaTransaction().updateSchemaStatus(a, SchemaStatus.CREATED); + this.params().schemaTransaction().updateSchemaStatus(b, SchemaStatus.CREATED); + } + } + + @Test + public void testOnlyEdgeCandidateDeletingReturnsEmpty() { + SchemaManager schema = graph().schema(); + schema.edgeLabel("deletingEdge").link("coverageA", "coverageB") + .properties("coverageName").create(); + schema.indexLabel("deletingEdgeByName").onE("deletingEdge") + .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 edge = a.addEdge("deletingEdge", b, "coverageName", "same"); + this.commitTx(); + SchemaLabel label = schema.getEdgeLabel("deletingEdge"); + try { + this.params().schemaTransaction().updateSchemaStatus(label, SchemaStatus.DELETING); + Assert.assertEquals(ImmutableSet.of(), graph().traversal().E() + .has("coverageName", "same").toSet()); + this.assertEmptyPage(this.nameQuery(HugeType.EDGE)); + ConditionQuery query = this.nameQuery(HugeType.EDGE); + query.showDeleting(true); + Assert.assertEquals(ImmutableSet.of(edge), ImmutableSet.copyOf(graph().edges(query))); + schema.indexLabel("deletingEdgeByName").remove(); + Assert.assertEquals(ImmutableSet.of(), graph().traversal().E() + .has("coverageName", "same").toSet()); + Assert.assertThrows(NoIndexException.class, () -> graph().edges(query).hasNext()); + } finally { + this.params().schemaTransaction().updateSchemaStatus(label, SchemaStatus.CREATED); + } + Assert.assertThrows(NoIndexException.class, () -> graph().traversal().E() + .has("coverageName", "same").toSet()); + } + + @Test + public void testOnlyHiddenCandidateReturnsEmpty() { + SchemaManager schema = graph().schema(); + schema.propertyKey("hiddenName").asText().create(); + VertexLabel hidden = schema.vertexLabel("~coverageHidden") + .properties("hiddenName").useAutomaticId().build(); + this.params().schemaTransaction().addVertexLabel(hidden); + try { + schema.indexLabel("hiddenByName").onV("~coverageHidden") + .by("hiddenName").secondary().create(); + Vertex vertex = this.params().graphTransaction().addVertex( + this.params().graphTransaction().constructVertex( + false, T.label, "~coverageHidden", "hiddenName", "same")); + this.commitTx(); + Assert.assertEquals(ImmutableSet.of(), graph().traversal().V() + .has("hiddenName", "same").toSet()); + ConditionQuery query = new ConditionQuery(HugeType.VERTEX); + query.query(Condition.eq(graph().propertyKey("hiddenName").id(), "same")); + this.assertEmptyPage(query.copy()); + query.showHidden(true); + Assert.assertEquals(ImmutableSet.of(vertex), ImmutableSet.copyOf(graph().vertices(query))); + schema.indexLabel("hiddenByName").remove(); + Assert.assertEquals(ImmutableSet.of(), graph().traversal().V() + .has("hiddenName", "same").toSet()); + Assert.assertThrows(NoIndexException.class, () -> graph().vertices(query).hasNext()); + } finally { + // System schema cannot be deleted individually. Reset the test + // graph so the hidden label cannot retain this test's property key. + CoreTestSuite.clear(); + } + } + + @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 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(); + 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()); + + 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()); + + SchemaLabel subA = schema.getEdgeLabel("subA"); + try { + this.params().schemaTransaction().updateSchemaStatus(subA, SchemaStatus.DELETING); + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.DELETING); + schema.indexLabel("subAByName").remove(); + schema.indexLabel("subBByName").remove(); + // The visible base label has no direct records of its own. + Assert.assertEquals(ImmutableSet.of(), graph().traversal().E() + .has("coverageName", "same").toSet()); + } finally { + this.params().schemaTransaction().updateSchemaStatus(subA, SchemaStatus.CREATED); + this.params().schemaTransaction().updateSchemaStatus(deleting, SchemaStatus.CREATED); + } + } + + @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 void assertEmptyPage(ConditionQuery query) { + if (!storeFeatures().supportsQueryByPage()) { + return; + } + query.limit(1); + query.page(""); + Iterator iterator = query.resultType().isVertex() ? graph().vertices(query) : + graph().edges(query); + try { + Assert.assertFalse(iterator.hasNext()); + Assert.assertNull(PageInfo.pageInfo(iterator)); + } finally { + CloseableIterator.closeIterator(iterator); + } + } + + 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 8268291555..63cbc5c844 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 @@ -1351,7 +1351,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()); @@ -1363,7 +1363,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()); } @@ -1386,23 +1386,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(); @@ -1413,19 +1413,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()); } @@ -1450,32 +1450,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)); @@ -1487,26 +1487,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()); } @@ -1520,7 +1520,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()); @@ -1532,7 +1532,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()); } @@ -1572,7 +1572,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()); @@ -1591,10 +1591,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(); @@ -1606,9 +1606,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()); } @@ -1631,23 +1631,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(); @@ -1676,26 +1676,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(); @@ -1707,22 +1707,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()); } @@ -1747,32 +1747,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)); @@ -1804,38 +1804,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)); @@ -1848,33 +1848,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()); } @@ -1888,7 +1888,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()); @@ -1905,11 +1905,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()); @@ -1922,7 +1922,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()); } @@ -1971,7 +1971,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)); @@ -1984,7 +1984,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()); } @@ -2017,23 +2017,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(); @@ -2045,19 +2045,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()); } @@ -2094,32 +2094,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)); @@ -2132,26 +2132,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()); } @@ -2168,7 +2168,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)); @@ -2181,7 +2181,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()); } @@ -3670,15 +3670,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(); }); } @@ -3694,41 +3694,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")); } @@ -5009,9 +5009,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()); } @@ -5026,10 +5026,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()); } @@ -5079,7 +5079,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")); @@ -5144,41 +5144,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()); @@ -5191,11 +5191,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()); @@ -5214,11 +5214,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()); @@ -5229,11 +5229,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()); } @@ -5261,7 +5261,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<>(); @@ -5285,7 +5285,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()); @@ -5299,7 +5299,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()); } @@ -5317,7 +5317,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()); } @@ -5611,47 +5611,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()); @@ -6466,15 +6466,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")); @@ -6497,7 +6497,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")); } @@ -6668,7 +6668,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")); } @@ -6763,29 +6763,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")); } @@ -6799,18 +6799,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")); } @@ -6824,18 +6824,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")); } @@ -8965,17 +8965,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); @@ -9010,21 +9001,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()); @@ -9039,20 +9030,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()); @@ -9353,36 +9344,41 @@ public void testNegativeLabelAfterAdjacencyPreservesSourceCandidates() { "age", 20, "city", "Beijing"); Vertex person = graph.addVertex(T.label, "person", "name", "loop-person", "age", 20, "city", "Beijing"); - fan.addEdge("fanLoop", fan); + Edge fanEdge = fan.addEdge("fanLoop", fan); Edge personEdge = person.addEdge("personLoop", person); this.commitTx(); GraphTraversalSource g = graph.traversal(); - // The existing source query uses only indexed labels. #3201 tracks - // complete cross-label candidates; a later label must not change this - // source query's behavior solely because it follows an adjacency step. + // A downstream label filter cannot repair incomplete source coverage. + Assert.assertThrows(NoIndexException.class, () -> + g.V().has("city", "Beijing").out().hasLabel(P.neq("author")).toSet()); + for (String label : new String[]{"fan", "follower", "returnFan"}) { + graph.schema().indexLabel(label + "ByCity").onV(label) + .by("city").secondary().create(); + } Set source = g.V().has("city", "Beijing").toSet(); - Assert.assertEquals(ImmutableSet.of(person), source); + Assert.assertEquals(ImmutableSet.of(person, fan), source); Assert.assertEquals(source, g.V().has("city", "Beijing").out().toSet()); - Assert.assertEquals(source, g.V().has("city", "Beijing") + Assert.assertEquals(ImmutableSet.of(person), g.V().has("city", "Beijing") .out().hasLabel("person").toSet()); Assert.assertEquals(source, g.V().has("city", "Beijing") .out().hasLabel(P.neq("author")).toSet()); Assert.assertEquals(source, g.V().has("city", "Beijing") .out().hasLabel(P.neq("author")).dedup().toSet()); - Assert.assertEquals(ImmutableList.of(person), g.V().has("city", "Beijing") + Assert.assertEquals(ImmutableList.of(fan, person), g.V().has("city", "Beijing") .out().hasLabel(P.neq("author")).order().by("name").toList()); - Assert.assertEquals(source, g.V().has("city", "Beijing") - .out().hasLabel(P.neq("author")).tail(1).toSet()); + Assert.assertEquals(ImmutableSet.of(person), g.V().has("city", "Beijing") + .out().hasLabel(P.neq("author")).order().by("name").tail(1).toSet()); Assert.assertEquals(source, g.V().has("city", "Beijing") .out().hasLabel(P.neq("author")).fold().unfold().toSet()); - Assert.assertEquals(ImmutableList.of(20), g.V().has("city", "Beijing") + Assert.assertEquals(ImmutableList.of(20, 20), g.V().has("city", "Beijing") .out().hasLabel(P.neq("author")).values("age").is(P.gt(1)).toList()); - Assert.assertEquals(ImmutableList.of(1), g.V().has("city", "Beijing") + Assert.assertEquals(ImmutableList.of(1, 1), g.V().has("city", "Beijing") .out().hasLabel(P.neq("author")).constant(1).toList()); Assert.assertEquals(source, g.V().has("city", "Beijing") .out().hasLabel(P.neq("author")).sample(2).toSet()); - Assert.assertEquals(ImmutableMap.of("person", ImmutableList.of(person)), g.V() + Assert.assertEquals(ImmutableMap.of("person", ImmutableList.of(person), + "returnFan", ImmutableList.of(fan)), g.V() .has("city", "Beijing").out().hasLabel(P.neq("author")).group().by(T.label).next()); Assert.assertEquals(source, g.V().has("city", "Beijing") .out().hasLabel(P.neq("author")).aggregate("x").toSet()); @@ -9396,13 +9392,13 @@ public void testNegativeLabelAfterAdjacencyPreservesSourceCandidates() { .out().inE().outV().hasLabel(P.neq("author")).toSet()); Assert.assertEquals(source, g.V().has("city", "Beijing") .where(__.out().in().hasLabel(P.neq("author"))).toSet()); - Assert.assertEquals(ImmutableSet.of(personEdge), g.V().has("city", "Beijing") + Assert.assertEquals(ImmutableSet.of(personEdge, fanEdge), g.V().has("city", "Beijing") .outE().hasLabel(P.neq("knows")).toSet()); - Assert.assertEquals(ImmutableSet.of(personEdge), g.V().has("city", "Beijing") + Assert.assertEquals(ImmutableSet.of(personEdge, fanEdge), g.V().has("city", "Beijing") .outE().hasLabel(P.neq("knows")).dedup().toSet()); Assert.assertEquals(ImmutableSet.of(personEdge), g.V().has("city", "Beijing") - .outE().hasLabel(P.neq("knows")).tail(1).toSet()); - Assert.assertEquals(ImmutableSet.of(personEdge), g.V().has("city", "Beijing") + .outE().hasLabel(P.neq("knows")).order().by(T.label).tail(1).toSet()); + Assert.assertEquals(ImmutableSet.of(personEdge, fanEdge), g.V().has("city", "Beijing") .outE().hasLabel(P.neq("knows")).fold().unfold().toSet()); Assert.assertEquals(source, g.V().has("city", "Beijing") .where(__.out().hasLabel(P.neq("author")).tail(1)).toSet()); @@ -9553,6 +9549,12 @@ public void testNegativeLabelAfterElementChange() { source.addEdge("link", target); this.commitTx(); GraphTraversalSource g = graph.traversal(); + Assert.assertThrows(NoIndexException.class, () -> + g.V().has("city", "Beijing").out().hasLabel(P.neq("indexed")).toSet()); + for (String label : new String[]{"person", "fan", "follower"}) { + graph.schema().indexLabel(label + "ByCity").onV(label) + .by("city").secondary().create(); + } for (GraphTraversal query : ImmutableList.of( g.V().has("city", "Beijing").out().hasLabel(P.neq("indexed")), g.V().has("city", "Beijing").out().where(__.not(__.hasLabel("indexed"))))) { @@ -9679,6 +9681,12 @@ public void testRangeIndexAfterElementChangeWithTerminalSteps() { source.addEdge("link", target); this.commitTx(); GraphTraversalSource g = graph.traversal(); + Assert.assertThrows(NoIndexException.class, () -> g.V().has("age", P.gte(60)) + .out().hasLabel(P.neq("indexed")).count().next()); + for (String label : new String[]{"person", "author", "fan", "follower", "unindexed"}) { + graph.schema().indexLabel(label + "ByAge").onV(label) + .by("age").range().create(); + } List> queries = ImmutableList.of( g.V().has("age", P.gte(60)).out().hasLabel(P.neq("indexed")).count(), g.V().has("age", P.gte(60)).out().where(__.not(__.hasLabel("indexed"))).count(), @@ -10102,19 +10110,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(); @@ -10151,6 +10159,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); @@ -10588,8 +10605,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") 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 97447f08ac..75a02f5469 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 @@ -565,6 +565,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() @@ -573,6 +577,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") @@ -898,6 +904,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") @@ -912,12 +928,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) @@ -985,5 +1010,8 @@ public void initSinkSchema() { schema.indexLabel("loopsByName").onV("loops") .secondary().by("name") .ifNotExist().create(); + schema.indexLabel("messageByName").onV("message") + .secondary().by("name") + .ifNotExist().create(); } }