From f3cdce14d876121a810bed324e41d0ea467718ed Mon Sep 17 00:00:00 2001 From: Oleksandr Porunov Date: Wed, 7 Jun 2023 22:03:03 +0100 Subject: [PATCH] Enable multiQuery optimization for PropertyMapStep and ElementMapStep Adds possibility to fetch properties and labels of vertices using valueMap, elementMap, propertyMap steps. Adds fetching modes to properties, values, valueMap, elementMap, propertyMap steps to be able to preFetch all properties (single slice query) or only required properties (separate slice query per each requested property). Fixes #2444 Signed-off-by: Oleksandr Porunov --- docs/configs/janusgraph-cfg.md | 3 +- .../janusgraph/graphdb/JanusGraphTest.java | 392 +++++++++++++++++- .../janusgraph/core/TransactionBuilder.java | 14 +- .../GraphDatabaseConfiguration.java | 22 +- .../graphdb/database/StandardJanusGraph.java | 6 +- .../graphdb/olap/computer/FulgoraUtil.java | 4 +- ...java => JanusGraphMultiQueriableUtil.java} | 128 +++--- .../step/JanusGraphElementMapStep.java | 172 ++++++++ .../step/JanusGraphPropertiesStep.java | 40 +- .../step/JanusGraphPropertyMapStep.java | 211 ++++++++++ .../step/fetcher/LabelStepBatchFetcher.java | 48 +++ ...stractJanusGraphMixedIndexAggStrategy.java | 2 +- ...texHasUniquePropertyOptimizerStrategy.java | 2 +- .../AdjacentVertexOptimizerStrategy.java | 2 +- ...JanusGraphLocalQueryOptimizerStrategy.java | 158 ------- ...usGraphMultiQueriableCreationStrategy.java | 208 ++++++++++ .../JanusGraphMultiQueryStrategy.java | 2 +- .../MultiQueryPropertiesStrategyMode.java | 47 +++ .../StandardTransactionBuilder.java | 60 ++- .../transaction/TransactionConfiguration.java | 8 +- ...phMultiQueriableCreationStrategyTest.java} | 2 +- .../JanusGraphMultiQueryStrategyTest.java | 6 +- .../optimize/JanusGraphStepStrategyTest.java | 6 +- 23 files changed, 1254 insertions(+), 289 deletions(-) rename janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/{strategy/JanusGraphHasStepStrategy.java => JanusGraphMultiQueriableUtil.java} (50%) create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphElementMapStep.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphPropertyMapStep.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/fetcher/LabelStepBatchFetcher.java delete mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphLocalQueryOptimizerStrategy.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphMultiQueriableCreationStrategy.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/MultiQueryPropertiesStrategyMode.java rename janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/{JanusGraphLocalQueryOptimizerStrategyTest.java => JanusGraphMultiQueriableCreationStrategyTest.java} (99%) diff --git a/docs/configs/janusgraph-cfg.md b/docs/configs/janusgraph-cfg.md index 44e0dfa3b07..d035a9414d4 100644 --- a/docs/configs/janusgraph-cfg.md +++ b/docs/configs/janusgraph-cfg.md @@ -362,7 +362,7 @@ Configuration options to configure batch queries optimization behavior | Name | Description | Datatype | Default Value | Mutability | | ---- | ---- | ---- | ---- | ---- | | query.batch.enabled | Whether traversal queries should be batched when executed against the storage backend. This can lead to significant performance improvement if there is a non-trivial latency to the backend. If `false` then all other configuration options under `query.batch` namespace are ignored. | Boolean | true | MASKABLE | -| query.batch.has-step-mode | Properties pre-fetching mode for `has` step. Used only when query.batch.enabled is `true`.
Supported modes:
- `all_properties` Pre-fetch all vertex properties on any property access
- `required_properties_only` Pre-fetch necessary vertex properties for the whole chain of foldable `has` steps
- `required_and_next_properties` Prefetch the same properties as with `required_properties_only` mode, but also prefetch +| query.batch.has-step-mode | Properties pre-fetching mode for `has` step. Used only when query.batch.enabled is `true`.
Supported modes:
- `all_properties` Pre-fetch all vertex properties on any property access (fetches all vertex properties in a single slice query)
- `required_properties_only` Pre-fetch necessary vertex properties for the whole chain of foldable `has` steps (uses a separate slice query per each required property)
- `required_and_next_properties` Prefetch the same properties as with `required_properties_only` mode, but also prefetch properties which may be needed in the next properties access step like `values`, `properties,` `valueMap`, `elementMap`, or `propertyMap`. In case the next step is not one of those properties access steps then this mode behaves same as `required_properties_only`. In case the next step is one of the properties access steps with limited scope of properties, those properties will be @@ -372,6 +372,7 @@ behaves same as `all_properties`.
- `required_and_next_properties_or_all` Pre `values`, `properties,` `valueMap`, `elementMap`, or `propertyMap` then acts like `all_properties`.
- `none` Skips `has` step batch properties pre-fetch optimization.
| String | required_and_next_properties | MASKABLE | | query.batch.limited | Configure a maximum batch size for queries against the storage backend. This can be used to ensure responsiveness if batches tend to grow very large. The used batch size is equivalent to the barrier size of a preceding `barrier()` step. If a step has no preceding `barrier()`, the default barrier of TinkerPop will be inserted. This option only takes effect if `query.batch.enabled` is `true`. | Boolean | true | MASKABLE | | query.batch.limited-size | Default batch size (barrier() step size) for queries. This size is applied only for cases where `LazyBarrierStrategy` strategy didn't apply `barrier` step and where user didn't apply barrier step either. This option is used only when `query.batch.limited` is `true`. Notice, value `2147483647` is considered to be unlimited. | Integer | 2500 | MASKABLE | +| query.batch.properties-mode | Properties pre-fetching mode for `values`, `properties`, `valueMap`, `propertyMap`, `elementMap` steps. Used only when query.batch.enabled is `true`.
Supported modes:
- `all_properties` Pre-fetch all vertex properties on any property access (fetches all vertex properties in a single slice query)
- `required_properties_only` Pre-fetch necessary vertex properties only (uses a separate slice query per each required property)
- `none` Skips vertex properties pre-fetching optimization.
| String | required_properties_only | MASKABLE | | query.batch.repeat-step-mode | Batch mode for `repeat` step. Used only when query.batch.enabled is `true`.
These modes are controlling how the child steps with batch support are behaving if they placed to the start of the `repeat`, `emit`, or `until` traversals.
Supported modes:
- `closest_repeat_parent` Child start steps are receiving vertices for batching from the closest `repeat` step parent only.
- `all_repeat_parents` Child start steps are receiving vertices for batching from all `repeat` step parents.
- `starts_only_of_all_repeat_parents` Child start steps are receiving vertices for batching from the closest `repeat` step parent (both for the parent start and for next iterations) and also from all `repeat` step parents for the parent start. | String | all_repeat_parents | MASKABLE | ### schema diff --git a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java index d763bf8d892..51547ff197b 100644 --- a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java +++ b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java @@ -132,6 +132,7 @@ import org.janusgraph.graphdb.serializer.SpecialIntSerializer; import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphHasStep; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryHasStepStrategyMode; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryPropertiesStrategyMode; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryStrategyRepeatStepMode; import org.janusgraph.graphdb.transaction.StandardJanusGraphTx; import org.janusgraph.graphdb.types.CompositeIndexType; @@ -217,6 +218,7 @@ import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.MAX_COMMIT_TIME; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.METRICS_MERGE_STORES; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.PARALLEL_BACKEND_OPS; +import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.PROPERTIES_BATCH_MODE; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.PROPERTY_PREFETCHING; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.REPEAT_STEP_BATCH_MODE; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.REPLACE_INSTANCE_IF_EXISTS; @@ -4923,6 +4925,7 @@ public void testLimitBatchSizeForMultiQuery() { TraversalMetrics profile; testLimitBatchSizeForHasStep(numV, barrierSize, limit, bs, cs); + testLimitBatchSizeForPropertySteps(numV, barrierSize, limit, cs); // test batching for `out()` profile = testLimitedBatch(() -> graph.traversal().V(bs).barrier(barrierSize).out()); @@ -4933,15 +4936,6 @@ public void testLimitBatchSizeForMultiQuery() { profile = testLimitedBatch(() -> graph.traversal().V(bs).barrier(barrierSize).out().limit(limit)); assertEquals((int) Math.ceil((double) limit / barrierSize), countBackendQueriesOfSize(barrierSize * 2, profile.getMetrics())); - // test batching for `values()` - profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo")); - assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); - assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); - - // test early abort with limit for `values()` - profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo").limit(limit)); - assertEquals((int) Math.ceil((double) limit / barrierSize), countBackendQueriesOfSize(barrierSize, profile.getMetrics())); - // test batching with unlimited batch size profile = testLimitedBatch(() -> graph.traversal().V(bs).barrier(barrierSize).out(), option(USE_MULTIQUERY), true, option(LIMITED_BATCH), false); assertEquals(0, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); @@ -5044,6 +5038,386 @@ public void testLimitBatchSizeForMultiQuery() { testLimitBatchSizeForMultiQueryOfConnectiveSteps(bs, barrierSize, limit); } + private void testLimitBatchSizeForPropertySteps(int numV, int barrierSize, int limit, JanusGraphVertex[] cs){ + TraversalMetrics profile; + + // Test required properties prefetching only prefetching + + // test batching for `values()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `values()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals((int) Math.ceil((double) limit / barrierSize), countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + + // test batching for `values()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `values()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals((int) Math.ceil((double) limit / barrierSize), countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + + // test batching for `properties()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).properties("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `values()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).properties("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals((int) Math.ceil((double) limit / barrierSize), countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + + // test batching for `valueMap()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `valueMap()`. Limit is going to be placed before valueMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(1, countBackendQueriesOfSize(limit, profile.getMetrics())); + + // test batching for `valueMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `valueMap()`. Limit is going to be placed before valueMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(1, countBackendQueriesOfSize(limit, profile.getMetrics())); + + // test batching for `propertyMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).propertyMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `propertyMap()`. Limit is going to be placed before propertyMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).propertyMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(1, countBackendQueriesOfSize(limit, profile.getMetrics())); + + // test batching for `elementMap()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(6, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(2, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `elementMap()`. Limit is going to be placed before elementMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(2, countBackendQueriesOfSize(limit, profile.getMetrics())); + + // test batching for `elementMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(6, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(2, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `elementMap()`. Limit is going to be placed before elementMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + assertEquals(2, countBackendQueriesOfSize(limit, profile.getMetrics())); + + // Test all properties prefetching + + int propertiesCount = (int) (graph.traversal().V(cs).values().count().next() / numV); + + // test batching for `values()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize * propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize((numV - 3 * barrierSize) * propertiesCount, profile.getMetrics())); + + // test early abort with limit for `values()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals((int) Math.ceil((double) limit / barrierSize), countBackendQueriesOfSize(barrierSize*propertiesCount, profile.getMetrics())); + + // test batching for `values()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize * propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize((numV - 3 * barrierSize) * propertiesCount, profile.getMetrics())); + + // test early abort with limit for `values()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals((int) Math.ceil((double) limit / barrierSize), countBackendQueriesOfSize(barrierSize*propertiesCount, profile.getMetrics())); + + // test batching for `properties()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).properties("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize * propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize((numV - 3 * barrierSize) * propertiesCount, profile.getMetrics())); + + // test early abort with limit for `values()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).properties("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals((int) Math.ceil((double) limit / barrierSize), countBackendQueriesOfSize(barrierSize*propertiesCount, profile.getMetrics())); + + // test batching for `valueMap()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize * propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize((numV - 3 * barrierSize) * propertiesCount, profile.getMetrics())); + + // test early abort with limit for `valueMap()`. Limit is going to be placed before valueMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(1, countBackendQueriesOfSize(limit*propertiesCount, profile.getMetrics())); + + // test batching for `valueMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize * propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize((numV - 3 * barrierSize) * propertiesCount, profile.getMetrics())); + + // test early abort with limit for `valueMap()`. Limit is going to be placed before valueMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(1, countBackendQueriesOfSize(limit*propertiesCount, profile.getMetrics())); + + // test batching for `propertyMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).propertyMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize * propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize((numV - 3 * barrierSize) * propertiesCount, profile.getMetrics())); + + // test early abort with limit for `propertyMap()`. Limit is going to be placed before propertyMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).propertyMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(1, countBackendQueriesOfSize(limit*propertiesCount, profile.getMetrics())); + + // test batching for `elementMap()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize * propertiesCount, profile.getMetrics())); + assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize((numV - 3 * barrierSize) * propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `elementMap()`. Limit is going to be placed before elementMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(1, countBackendQueriesOfSize(limit*propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(limit, profile.getMetrics())); + + // test batching for `elementMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(3, countBackendQueriesOfSize(barrierSize * propertiesCount, profile.getMetrics())); + assertEquals(3, countBackendQueriesOfSize(barrierSize, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize((numV - 3 * barrierSize) * propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(numV - 3 * barrierSize, profile.getMetrics())); + + // test early abort with limit for `elementMap()`. Limit is going to be placed before elementMap. + // Thus, `barrier(barrierSize)` is ignored and instead the default barrier(2500) is used. + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo").limit(limit), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName()); + assertEquals(1, countBackendQueriesOfSize(limit*propertiesCount, profile.getMetrics())); + assertEquals(1, countBackendQueriesOfSize(limit, profile.getMetrics())); + + // Test disabled properties preFetching mode + + // test no batching used for `values()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `values()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).values("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `properties()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).properties("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `properties()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).properties("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `valueMap()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `valueMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).valueMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `propertyMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).propertyMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `propertyMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).propertyMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `elementMap()` + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), false, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + + // test no batching used for `elementMap()` (enabled prefetching should not influence the query) + profile = testLimitedBatch(() -> graph.traversal().V(cs).barrier(barrierSize).elementMap("foo"), + option(USE_MULTIQUERY), true, + option(LIMITED_BATCH), true, + option(PROPERTY_PREFETCHING), true, + option(PROPERTIES_BATCH_MODE), MultiQueryPropertiesStrategyMode.NONE.getConfigName()); + assertEquals(0, countBackendQueriesOfSize(s -> true, profile.getMetrics())); + } + private void testLimitBatchSizeForHasStep(int numV, int barrierSize, int limit, JanusGraphVertex[] bs, JanusGraphVertex[] cs){ TraversalMetrics profile; diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/TransactionBuilder.java b/janusgraph-core/src/main/java/org/janusgraph/core/TransactionBuilder.java index da7eb0e4083..dba638eb913 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/core/TransactionBuilder.java +++ b/janusgraph-core/src/main/java/org/janusgraph/core/TransactionBuilder.java @@ -16,6 +16,7 @@ import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryHasStepStrategyMode; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryPropertiesStrategyMode; import java.time.Instant; @@ -68,7 +69,7 @@ public interface TransactionBuilder { TransactionBuilder propertyPrefetching(boolean enabled); /** - * Enable or disable multi-query, i.e. query.batch + * Enable or disable multi-query, i.e. `query.batch.enabled` * * @param enabled * @return Object containing properties that will enable/disable multi-query @@ -151,12 +152,21 @@ public interface TransactionBuilder { /** * Sets `has` step strategy mode. *

- * Doesn't have any effect if multi-query was disabled via config `query.batch`. + * Doesn't have any effect if multi-query was disabled via config `query.batch.enabled = false`. * * @return Object with the set `has` step strategy mode settings */ TransactionBuilder setHasStepStrategyMode(MultiQueryHasStepStrategyMode hasStepStrategyMode); + /** + * Sets properties strategy mode. + *

+ * Doesn't have any effect if multi-query was disabled via config `query.batch.enabled = false`. + * + * @return Object with the set properties strategy mode settings + */ + TransactionBuilder setPropertiesStrategyMode(MultiQueryPropertiesStrategyMode propertiesStrategyMode); + /** * Sets the group name for this transaction which provides a way for gathering * reporting on multiple transactions into one group. diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/configuration/GraphDatabaseConfiguration.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/configuration/GraphDatabaseConfiguration.java index b10f6092810..15a0aab0310 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/configuration/GraphDatabaseConfiguration.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/configuration/GraphDatabaseConfiguration.java @@ -58,6 +58,7 @@ import org.janusgraph.graphdb.query.index.BruteForceIndexSelectionStrategy; import org.janusgraph.graphdb.query.index.IndexSelectionStrategy; import org.janusgraph.graphdb.query.index.ThresholdBasedIndexSelectionStrategy; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryPropertiesStrategyMode; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryStrategyRepeatStepMode; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryHasStepStrategyMode; import org.janusgraph.graphdb.transaction.StandardTransactionBuilder; @@ -331,8 +332,8 @@ public class GraphDatabaseConfiguration { public static final ConfigOption HAS_STEP_BATCH_MODE = new ConfigOption<>(QUERY_BATCH_NS,"has-step-mode", String.format("Properties pre-fetching mode for `has` step. Used only when "+USE_MULTIQUERY.toStringWithoutRoot()+" is `true`.
" + "Supported modes:
" + - "- `%s` Pre-fetch all vertex properties on any property access
" + - "- `%s` Pre-fetch necessary vertex properties for the whole chain of foldable `has` steps
" + + "- `%s` Pre-fetch all vertex properties on any property access (fetches all vertex properties in a single slice query)
" + + "- `%s` Pre-fetch necessary vertex properties for the whole chain of foldable `has` steps (uses a separate slice query per each required property)
" + "- `%s` Prefetch the same properties as with `%s` mode, but also prefetch\n" + "properties which may be needed in the next properties access step like `values`, `properties,` `valueMap`, `elementMap`, or `propertyMap`.\n" + "In case the next step is not one of those properties access steps then this mode behaves same as `%s`.\n" + @@ -355,6 +356,17 @@ public class GraphDatabaseConfiguration { MultiQueryHasStepStrategyMode.NONE.getConfigName()), ConfigOption.Type.MASKABLE, MultiQueryHasStepStrategyMode.REQUIRED_AND_NEXT_PROPERTIES.getConfigName()); + public static final ConfigOption PROPERTIES_BATCH_MODE = new ConfigOption<>(QUERY_BATCH_NS,"properties-mode", + String.format("Properties pre-fetching mode for `values`, `properties`, `valueMap`, `propertyMap`, `elementMap` steps. Used only when "+USE_MULTIQUERY.toStringWithoutRoot()+" is `true`.
" + + "Supported modes:
" + + "- `%s` Pre-fetch all vertex properties on any property access (fetches all vertex properties in a single slice query)
" + + "- `%s` Pre-fetch necessary vertex properties only (uses a separate slice query per each required property)
" + + "- `%s` Skips vertex properties pre-fetching optimization.
", + MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.getConfigName(), + MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName(), + MultiQueryPropertiesStrategyMode.NONE.getConfigName()), + ConfigOption.Type.MASKABLE, MultiQueryPropertiesStrategyMode.REQUIRED_PROPERTIES_ONLY.getConfigName()); + // ################ SCHEMA ####################### // ################################################ @@ -1326,6 +1338,7 @@ public boolean apply(@Nullable String s) { private String metricsPrefix; private String unknownIndexKeyName; private MultiQueryHasStepStrategyMode hasStepStrategyMode; + private MultiQueryPropertiesStrategyMode propertiesStrategyMode; private StoreFeatures storeFeatures = null; @@ -1444,6 +1457,10 @@ public MultiQueryHasStepStrategyMode hasStepStrategyMode() { return hasStepStrategyMode; } + public MultiQueryPropertiesStrategyMode propertiesStrategyMode() { + return propertiesStrategyMode; + } + public boolean adjustQueryLimit() { return adjustQueryLimit; } @@ -1572,6 +1589,7 @@ private void preLoadConfiguration() { limitedBatchSize = configuration.get(LIMITED_BATCH_SIZE); repeatStepMode = selectExactConfig(REPEAT_STEP_BATCH_MODE, MultiQueryStrategyRepeatStepMode.values()); hasStepStrategyMode = selectExactConfig(HAS_STEP_BATCH_MODE, MultiQueryHasStepStrategyMode.values()); + propertiesStrategyMode = selectExactConfig(PROPERTIES_BATCH_MODE, MultiQueryPropertiesStrategyMode.values()); indexSelectionStrategy = Backend.getImplementationClass(configuration, configuration.get(INDEX_SELECT_STRATEGY), REGISTERED_INDEX_SELECTION_STRATEGIES); diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/StandardJanusGraph.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/StandardJanusGraph.java index 9a52f2c2cd8..aa1dfeea07d 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/StandardJanusGraph.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/StandardJanusGraph.java @@ -65,8 +65,8 @@ import org.janusgraph.graphdb.database.cache.SchemaCache; import org.janusgraph.graphdb.database.idassigner.VertexIDAssigner; import org.janusgraph.graphdb.database.idhandling.IDHandler; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphMultiQueriableCreationStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphUnusedMultiQueryRemovalStrategy; -import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphHasStepStrategy; import org.janusgraph.util.IDUtils; import org.janusgraph.graphdb.database.index.IndexInfoRetriever; import org.janusgraph.graphdb.database.index.IndexUpdate; @@ -90,7 +90,6 @@ import org.janusgraph.graphdb.tinkerpop.optimize.strategy.AdjacentVertexHasUniquePropertyOptimizerStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.AdjacentVertexIsOptimizerStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphIoRegistrationStrategy; -import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphLocalQueryOptimizerStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphMixedIndexAggStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphMixedIndexCountStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphMultiQueryStrategy; @@ -146,13 +145,12 @@ public class StandardJanusGraph extends JanusGraphBlueprintsGraph { AdjacentVertexHasIdOptimizerStrategy.instance(), AdjacentVertexIsOptimizerStrategy.instance(), AdjacentVertexHasUniquePropertyOptimizerStrategy.instance(), - JanusGraphLocalQueryOptimizerStrategy.instance(), + JanusGraphMultiQueriableCreationStrategy.instance(), JanusGraphMultiQueryStrategy.instance(), JanusGraphUnusedMultiQueryRemovalStrategy.instance(), JanusGraphMixedIndexAggStrategy.instance(), JanusGraphMixedIndexCountStrategy.instance(), JanusGraphStepStrategy.instance(), - JanusGraphHasStepStrategy.instance(), JanusGraphIoRegistrationStrategy.instance()); //Register with cache diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/olap/computer/FulgoraUtil.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/olap/computer/FulgoraUtil.java index f4dcbf2af8e..be2874957bf 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/olap/computer/FulgoraUtil.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/olap/computer/FulgoraUtil.java @@ -31,7 +31,7 @@ import org.janusgraph.core.JanusGraphTransaction; import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphTraversalUtil; import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphVertexStep; -import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphLocalQueryOptimizerStrategy; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphMultiQueriableCreationStrategy; import java.util.List; import java.util.Optional; @@ -42,7 +42,7 @@ */ public class FulgoraUtil { - private static final TraversalStrategies FULGORA_STRATEGIES = TraversalStrategies.GlobalCache.getStrategies(Graph.class).clone().addStrategies(JanusGraphLocalQueryOptimizerStrategy.instance()); + private static final TraversalStrategies FULGORA_STRATEGIES = TraversalStrategies.GlobalCache.getStrategies(Graph.class).clone().addStrategies(JanusGraphMultiQueriableCreationStrategy.instance()); public static JanusGraphVertexStep getReverseJanusGraphVertexStep(final MessageScope.Local scope, final JanusGraphTransaction graph) { diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphHasStepStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueriableUtil.java similarity index 50% rename from janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphHasStepStrategy.java rename to janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueriableUtil.java index 23416cdf10b..baa64ad59fa 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphHasStepStrategy.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueriableUtil.java @@ -12,94 +12,42 @@ // See the License for the specific language governing permissions and // limitations under the License. -package org.janusgraph.graphdb.tinkerpop.optimize.strategy; +package org.janusgraph.graphdb.tinkerpop.optimize; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.ElementMapStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.NoOpBarrierStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertyMapStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IdentityStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SideEffectStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.ProfileStep; -import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; -import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; -import org.janusgraph.graphdb.database.StandardJanusGraph; -import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphTraversalUtil; +import org.janusgraph.graphdb.tinkerpop.optimize.step.HasStepFolder; import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphHasStep; import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphMultiQueryStep; -import org.janusgraph.graphdb.transaction.StandardJanusGraphTx; -import org.janusgraph.graphdb.transaction.TransactionConfiguration; +import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphPropertiesStep; +import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphVertexStep; +import org.janusgraph.graphdb.tinkerpop.optimize.step.MultiQueriable; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryHasStepStrategyMode; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; -public class JanusGraphHasStepStrategy extends AbstractTraversalStrategy implements TraversalStrategy.ProviderOptimizationStrategy { +/** + * Util tool to create and configure MultiQueriable steps + */ +public class JanusGraphMultiQueriableUtil { - private static final JanusGraphHasStepStrategy INSTANCE = new JanusGraphHasStepStrategy(); + private JanusGraphMultiQueriableUtil(){} - private static final Set> PRIORS = Collections.singleton(JanusGraphLocalQueryOptimizerStrategy.class); - - private JanusGraphHasStepStrategy() { - } - - @Override - public void apply(final Traversal.Admin traversal) { - if (!traversal.getGraph().isPresent()) - return; - - final StandardJanusGraph janusGraph = JanusGraphTraversalUtil.getJanusGraph(traversal); - if (janusGraph == null) { - return; - } - - final Optional tx = JanusGraphTraversalUtil.getJanusGraphTx(traversal); - final MultiQueryHasStepStrategyMode hasStepStrategyMode; - final int txVertexCacheSize; - final boolean hasPropertyPrefetching; - if(tx.isPresent()){ - TransactionConfiguration txConfig = tx.get().getConfiguration(); - hasStepStrategyMode = txConfig.getHasStepStrategyMode(); - txVertexCacheSize = txConfig.getVertexCacheSize(); - hasPropertyPrefetching = txConfig.hasPropertyPrefetching(); - } else { - GraphDatabaseConfiguration graphConfig = janusGraph.getConfiguration(); - hasStepStrategyMode = graphConfig.hasStepStrategyMode(); - txVertexCacheSize = graphConfig.getTxVertexCacheSize(); - hasPropertyPrefetching = graphConfig.hasPropertyPrefetching(); - } - - if(MultiQueryHasStepStrategyMode.NONE.equals(hasStepStrategyMode)){ - return; - } - - // if `hasPropertyPrefetching` is `true` than modes which don't fetch all properties are redundant because - // the cached slice queries for separate properties are not going to be used for a query which accesses all properties together. - // Thus, having anything than `ALL_PROPERTIES` - mean additional redundant requests. - // That's why we use `MultiQueryHasStepStrategyMode.ALL_PROPERTIES` whenever `hasPropertyPrefetching` is `true`. - applyJanusGraphHasSteps(traversal, txVertexCacheSize, - hasPropertyPrefetching ? MultiQueryHasStepStrategyMode.ALL_PROPERTIES : hasStepStrategyMode); - } - - private void applyJanusGraphHasSteps(final Traversal.Admin traversal, final int txVertexCacheSize, - final MultiQueryHasStepStrategyMode hasStepStrategyMode) { - TraversalHelper.getStepsOfAssignableClass(HasStep.class, traversal).forEach(originalStep -> { - if(originalStep instanceof JanusGraphHasStep){ - return; - } - final JanusGraphHasStep janusGraphHasStep = createJanusGraphHasStep(originalStep, txVertexCacheSize, hasStepStrategyMode); - TraversalHelper.replaceStep(originalStep, janusGraphHasStep, originalStep.getTraversal()); - }); - } - - private JanusGraphHasStep createJanusGraphHasStep(final HasStep originalStep, + public static JanusGraphHasStep createJanusGraphHasStep(final HasStep originalStep, final int txVertexCacheSize, final MultiQueryHasStepStrategyMode hasStepStrategyMode){ final JanusGraphHasStep janusGraphHasStep = new JanusGraphHasStep(originalStep); @@ -108,7 +56,7 @@ private JanusGraphHasStep createJanusGraphHasStep(final HasStep originalStep, janusGraphHasStep.setPrefetchAllPropertiesRequired(true); } else if(MultiQueryHasStepStrategyMode.REQUIRED_AND_NEXT_PROPERTIES.equals(hasStepStrategyMode) || MultiQueryHasStepStrategyMode.REQUIRED_AND_NEXT_PROPERTIES_OR_ALL.equals(hasStepStrategyMode)){ - Optional> optionalNextStepNeededProperties = findNextStepNeededProperties(originalStep); + Optional> optionalNextStepNeededProperties = findNextPropertiesStepNeededKeys(originalStep); if(optionalNextStepNeededProperties.isPresent()){ Set nextStepNeededProperties = optionalNextStepNeededProperties.get(); if(nextStepNeededProperties.isEmpty()){ @@ -125,7 +73,7 @@ private JanusGraphHasStep createJanusGraphHasStep(final HasStep originalStep, return janusGraphHasStep; } - private Optional> findNextStepNeededProperties(final HasStep originalStep){ + public static Optional> findNextPropertiesStepNeededKeys(final HasStep originalStep){ Step nextStep = originalStep.getNextStep(); while (nextStep instanceof NoOpBarrierStep || nextStep instanceof IdentityStep || nextStep instanceof ProfileStep || nextStep instanceof SideEffectStep @@ -144,12 +92,46 @@ private Optional> findNextStepNeededProperties(final HasStep origina return Optional.empty(); } - public static JanusGraphHasStepStrategy instance() { - return INSTANCE; + public static void updateStartOfLocalStepToMultiQueriable(final Traversal.Admin traversal, final LocalStep localStep, int txVertexCacheSize) { + final Traversal.Admin localTraversal = ((LocalStep) localStep).getLocalChildren().get(0); + final Step localStart = localTraversal.getStartStep(); + + if (localStart instanceof VertexStep) { + final JanusGraphVertexStep vertexStep = new JanusGraphVertexStep((VertexStep) localStart); + vertexStep.setBatchSize(txVertexCacheSize); + TraversalHelper.replaceStep(localStart, vertexStep, localTraversal); + + if (JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep)) { + HasStepFolder.foldInHasContainer(vertexStep, localTraversal, traversal); + HasStepFolder.foldInOrder(vertexStep, vertexStep.getNextStep(), localTraversal, traversal, false, null); + } + HasStepFolder.foldInRange(vertexStep, JanusGraphTraversalUtil.getNextNonIdentityStep(vertexStep), localTraversal, null); + unfoldLocalTraversal(traversal, localStep, localTraversal, vertexStep); + + } else if (localStart instanceof PropertiesStep) { + final JanusGraphPropertiesStep propertiesStep = new JanusGraphPropertiesStep((PropertiesStep) localStart); + propertiesStep.setBatchSize(txVertexCacheSize); + + TraversalHelper.replaceStep(localStart, propertiesStep, localTraversal); + + if (propertiesStep.getReturnType().forProperties()) { + HasStepFolder.foldInHasContainer(propertiesStep, localTraversal, traversal); + HasStepFolder.foldInOrder(propertiesStep, propertiesStep.getNextStep(), localTraversal, traversal, false, null); + } + HasStepFolder.foldInRange(propertiesStep, JanusGraphTraversalUtil.getNextNonIdentityStep(propertiesStep), localTraversal, null); + unfoldLocalTraversal(traversal, localStep, localTraversal, propertiesStep); + } } - @Override - public Set> applyPrior() { - return PRIORS; + private static void unfoldLocalTraversal(final Traversal.Admin traversal, + LocalStep localStep, Traversal.Admin localTraversal, + MultiQueriable vertexStep) { + assert localTraversal.asAdmin().getSteps().size() > 0; + if (localTraversal.asAdmin().getSteps().size() == 1) { + //Can replace the entire localStep by the vertex step in the outer traversal + assert localTraversal.getStartStep() == vertexStep; + vertexStep.setTraversal(traversal); + TraversalHelper.replaceStep(localStep, vertexStep, traversal); + } } } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphElementMapStep.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphElementMapStep.java new file mode 100644 index 00000000000..05211c2405b --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphElementMapStep.java @@ -0,0 +1,172 @@ +// Copyright 2023 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.tinkerpop.optimize.step; + +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.step.Profiling; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.ElementMapStep; +import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.BaseVertexQuery; +import org.janusgraph.core.JanusGraphProperty; +import org.janusgraph.graphdb.query.profile.QueryProfiler; +import org.janusgraph.graphdb.query.vertex.BasicVertexCentricQueryBuilder; +import org.janusgraph.graphdb.query.vertex.BasicVertexCentricQueryUtil; +import org.janusgraph.graphdb.tinkerpop.optimize.step.fetcher.LabelStepBatchFetcher; +import org.janusgraph.graphdb.tinkerpop.optimize.step.fetcher.PropertiesStepBatchFetcher; +import org.janusgraph.graphdb.tinkerpop.profile.TP3ProfileWrapper; +import org.janusgraph.graphdb.util.CopyStepUtil; +import org.janusgraph.graphdb.util.JanusGraphTraverserUtil; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class JanusGraphElementMapStep extends ElementMapStep implements Profiling, MultiQueriable>{ + + private boolean useMultiQuery = false; + private LabelStepBatchFetcher labelStepBatchFetcher; + private PropertiesStepBatchFetcher propertiesStepBatchFetcher; + private QueryProfiler queryProfiler = QueryProfiler.NO_OP; + private int batchSize = Integer.MAX_VALUE; + private boolean prefetchAllPropertiesRequired; + private final Set propertyKeysSet; + + public JanusGraphElementMapStep(ElementMapStep originalStep) { + super(originalStep.getTraversal(), originalStep.getPropertyKeys()); + CopyStepUtil.copyAbstractStepModifiableFields(originalStep, this); + if(originalStep.isOnGraphComputer()){ + onGraphComputer(); + } + propertyKeysSet = new HashSet<>(Arrays.asList(getPropertyKeys())); + if (originalStep instanceof JanusGraphElementMapStep) { + JanusGraphElementMapStep originalJanusGraphElementMapStep = (JanusGraphElementMapStep) originalStep; + setBatchSize(originalJanusGraphElementMapStep.batchSize); + setUseMultiQuery(originalJanusGraphElementMapStep.useMultiQuery); + } + } + + @Override + protected Map map(final Traverser.Admin traverser) { + if (useMultiQuery && traverser.get() instanceof Vertex) { + Vertex vertexToFetch = (Vertex) traverser.get(); + int loops = JanusGraphTraverserUtil.getLoops(traverser); + Map map = new LinkedHashMap(); + addElementProperties(map, vertexToFetch, loops); + addIncludedOptions(map, vertexToFetch, loops); + return (Map) map; + } + return super.map(traverser); + } + + private void addElementProperties(Map map, Vertex vertexToFetch, int loops){ + Iterator properties = propertiesStepBatchFetcher + .fetchData(getTraversal(), vertexToFetch, loops).iterator(); + while (properties.hasNext()) { + final Property property = properties.next(); + String propertyKey = property.key(); + if(!prefetchAllPropertiesRequired || propertyKeysSet.isEmpty() || propertyKeysSet.contains(propertyKey)){ + map.put(propertyKey, property.value()); + } + } + } + + private void addIncludedOptions(Map map, Vertex vertexToFetch, int loops){ + map.put(T.id, vertexToFetch.id()); + map.put(T.label, labelStepBatchFetcher.fetchData(getTraversal(), vertexToFetch, loops)); + } + + @Override + public void setMetrics(MutableMetrics metrics) { + queryProfiler = new TP3ProfileWrapper(metrics); + } + + @Override + public void setUseMultiQuery(boolean useMultiQuery) { + this.useMultiQuery = useMultiQuery; + if(useMultiQuery){ + if(propertiesStepBatchFetcher == null){ + propertiesStepBatchFetcher = new PropertiesStepBatchFetcher(JanusGraphElementMapStep.this::makePropertiesQuery, batchSize); + } + if(labelStepBatchFetcher == null){ + labelStepBatchFetcher = new LabelStepBatchFetcher(JanusGraphElementMapStep.this::makeLabelsQuery, batchSize); + } + } + } + + private Q makeLabelsQuery(Q query) { + return (Q) BasicVertexCentricQueryUtil.withLabelVertices((BasicVertexCentricQueryBuilder) query) + .profiler(queryProfiler); + } + + + private Q makePropertiesQuery(Q query) { + if(!prefetchAllPropertiesRequired){ + query.keys(getPropertyKeys()); + } + ((BasicVertexCentricQueryBuilder) query).profiler(queryProfiler); + return query; + } + + + @Override + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + if(labelStepBatchFetcher != null){ + labelStepBatchFetcher.setBatchSize(batchSize); + } + if(propertiesStepBatchFetcher != null){ + propertiesStepBatchFetcher.setBatchSize(batchSize); + } + } + + @Override + public void registerFirstNewLoopFutureVertexForPrefetching(Vertex futureVertex, int futureVertexTraverserLoop) { + if(useMultiQuery){ + labelStepBatchFetcher.registerFirstNewLoopFutureVertexForPrefetching(futureVertex); + propertiesStepBatchFetcher.registerFirstNewLoopFutureVertexForPrefetching(futureVertex); + } + } + + @Override + public void registerSameLoopFutureVertexForPrefetching(Vertex futureVertex, int futureVertexTraverserLoop) { + if(useMultiQuery){ + labelStepBatchFetcher.registerCurrentLoopFutureVertexForPrefetching(futureVertex, futureVertexTraverserLoop); + propertiesStepBatchFetcher.registerCurrentLoopFutureVertexForPrefetching(futureVertex, futureVertexTraverserLoop); + } + } + + @Override + public void registerNextLoopFutureVertexForPrefetching(Vertex futureVertex, int futureVertexTraverserLoop) { + if(useMultiQuery){ + labelStepBatchFetcher.registerNextLoopFutureVertexForPrefetching(futureVertex, futureVertexTraverserLoop); + propertiesStepBatchFetcher.registerNextLoopFutureVertexForPrefetching(futureVertex, futureVertexTraverserLoop); + } + } + + public boolean isPrefetchAllPropertiesRequired() { + return prefetchAllPropertiesRequired; + } + + public void setPrefetchAllPropertiesRequired(boolean prefetchAllPropertiesRequired) { + this.prefetchAllPropertiesRequired = prefetchAllPropertiesRequired; + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphPropertiesStep.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphPropertiesStep.java index c0e31976385..4196caabf7b 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphPropertiesStep.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphPropertiesStep.java @@ -40,13 +40,16 @@ import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphTraversalUtil; import org.janusgraph.graphdb.tinkerpop.optimize.step.fetcher.PropertiesStepBatchFetcher; import org.janusgraph.graphdb.tinkerpop.profile.TP3ProfileWrapper; -import org.janusgraph.graphdb.util.JanusGraphTraverserUtil; import org.janusgraph.graphdb.util.CopyStepUtil; +import org.janusgraph.graphdb.util.JanusGraphTraverserUtil; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Set; /** * @author Matthias Broecheler (me@matthiasb.com) @@ -59,10 +62,13 @@ public class JanusGraphPropertiesStep extends PropertiesStep implements Ha private PropertiesStepBatchFetcher propertiesStepBatchFetcher; private int batchSize = Integer.MAX_VALUE; + private boolean prefetchAllPropertiesRequired; + private final Set propertyKeysSet; public JanusGraphPropertiesStep(PropertiesStep originalStep) { super(originalStep.getTraversal(), originalStep.getReturnType(), originalStep.getPropertyKeys()); CopyStepUtil.copyAbstractStepModifiableFields(originalStep, this); + propertyKeysSet = new HashSet<>(Arrays.asList(getPropertyKeys())); if (originalStep instanceof JanusGraphPropertiesStep) { JanusGraphPropertiesStep originalJanusGraphPropertiesStep = (JanusGraphPropertiesStep) originalStep; @@ -105,8 +111,13 @@ public void registerNextLoopFutureVertexForPrefetching(Vertex futureVertex, int } private Q makeQuery(Q query) { - final String[] keys = getPropertyKeys(); - query.keys(keys); + return makeQuery(query, prefetchAllPropertiesRequired); + } + + private Q makeQuery(Q query, boolean prefetchAllPropertiesRequired) { + if(!prefetchAllPropertiesRequired){ + query.keys(getPropertyKeys()); + } for (final HasContainer condition : hasContainers) { query.has(condition.getKey(), JanusGraphPredicateUtils.convert(condition.getBiPredicate()), condition.getValue()); } @@ -117,11 +128,19 @@ private Q makeQuery(Q query) { } private Iterator convertIterator(Iterable iterable) { + return convertIterator(iterable, prefetchAllPropertiesRequired); + } + + private Iterator convertIterator(Iterable iterable, boolean prefetchAllPropertiesRequired) { + Iterator propertiesIt = iterable.iterator(); + if(prefetchAllPropertiesRequired && !propertyKeysSet.isEmpty()){ + propertiesIt = Iterators.filter(propertiesIt, property -> propertyKeysSet.contains(property.key())); + } if (getReturnType().forProperties()) { - return (Iterator) iterable.iterator(); + return (Iterator) propertiesIt; } assert getReturnType().forValues(); - return (Iterator) Iterators.transform(iterable.iterator(), Property::value); + return (Iterator) Iterators.transform(propertiesIt, Property::value); } /** @@ -136,8 +155,8 @@ protected Iterator flatMap(final Traverser.Admin traverser) { if (useMultiQuery && elementToFetchDataFor instanceof Vertex) { return convertIterator(propertiesStepBatchFetcher.fetchData(getTraversal(), (Vertex) elementToFetchDataFor, JanusGraphTraverserUtil.getLoops(traverser))); } else if (elementToFetchDataFor instanceof JanusGraphVertex || elementToFetchDataFor instanceof WrappedVertex) { - final JanusGraphVertexQuery query = makeQuery((JanusGraphTraversalUtil.getJanusGraphVertex(traverser)).query()); - return convertIterator(query.properties()); + final JanusGraphVertexQuery query = makeQuery((JanusGraphTraversalUtil.getJanusGraphVertex(traverser)).query(), false); + return convertIterator(query.properties(), false); } else { //It is some other element (edge or vertex property) Iterator iterator; @@ -252,4 +271,11 @@ public void setMetrics(MutableMetrics metrics) { queryProfiler = new TP3ProfileWrapper(metrics); } + public boolean isPrefetchAllPropertiesRequired() { + return prefetchAllPropertiesRequired; + } + + public void setPrefetchAllPropertiesRequired(boolean prefetchAllPropertiesRequired) { + this.prefetchAllPropertiesRequired = prefetchAllPropertiesRequired; + } } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphPropertyMapStep.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphPropertyMapStep.java new file mode 100644 index 00000000000..0e377d8e672 --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/JanusGraphPropertyMapStep.java @@ -0,0 +1,211 @@ +// Copyright 2023 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.tinkerpop.optimize.step; + +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.step.Profiling; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertyMapStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions; +import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.PropertyType; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.BaseVertexQuery; +import org.janusgraph.core.JanusGraphProperty; +import org.janusgraph.graphdb.query.profile.QueryProfiler; +import org.janusgraph.graphdb.query.vertex.BasicVertexCentricQueryBuilder; +import org.janusgraph.graphdb.query.vertex.BasicVertexCentricQueryUtil; +import org.janusgraph.graphdb.tinkerpop.optimize.step.fetcher.LabelStepBatchFetcher; +import org.janusgraph.graphdb.tinkerpop.optimize.step.fetcher.PropertiesStepBatchFetcher; +import org.janusgraph.graphdb.tinkerpop.profile.TP3ProfileWrapper; +import org.janusgraph.graphdb.util.CopyStepUtil; +import org.janusgraph.graphdb.util.JanusGraphTraverserUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class JanusGraphPropertyMapStep extends PropertyMapStep implements Profiling, MultiQueriable> { + + private boolean useMultiQuery = false; + private LabelStepBatchFetcher labelStepBatchFetcher; + private PropertiesStepBatchFetcher propertiesStepBatchFetcher; + private QueryProfiler queryProfiler = QueryProfiler.NO_OP; + private int batchSize = Integer.MAX_VALUE; + private boolean withIdsFetching; + private boolean withLabelsFetching; + private boolean prefetchAllPropertiesRequired; + private final Set propertyKeysSet; + + public JanusGraphPropertyMapStep(PropertyMapStep originalStep) { + super(originalStep.getTraversal(), originalStep.getReturnType(), originalStep.getPropertyKeys()); + CopyStepUtil.copyAbstractStepModifiableFields(originalStep, this); + tokens = originalStep.getIncludedTokens(); + withIdsFetching = includeToken(WithOptions.ids); + withLabelsFetching = includeToken(WithOptions.labels); + traversalRing = originalStep.getTraversalRing(); + traversalRing.getTraversals().forEach(this::integrateChild); + parameters = originalStep.getParameters(); + parameters.getTraversals().forEach(this::integrateChild); + propertyKeysSet = new HashSet<>(Arrays.asList(getPropertyKeys())); + + if (originalStep instanceof JanusGraphPropertyMapStep) { + JanusGraphPropertyMapStep originalJanusGraphPropertyMapStep = (JanusGraphPropertyMapStep) originalStep; + setBatchSize(originalJanusGraphPropertyMapStep.batchSize); + setUseMultiQuery(originalJanusGraphPropertyMapStep.useMultiQuery); + } + } + + @Override + public void configure(final Object... keyValues) { + super.configure(keyValues); + withIdsFetching = includeToken(WithOptions.ids); + withLabelsFetching = includeToken(WithOptions.labels); + createLabelFetcherIfNeeded(); + } + + @Override + protected Map map(final Traverser.Admin traverser) { + if (useMultiQuery && traverser.get() instanceof Vertex) { + Vertex vertexToFetch = (Vertex) traverser.get(); + int loops = JanusGraphTraverserUtil.getLoops(traverser); + Map map = new LinkedHashMap(); + addElementProperties(map, vertexToFetch, loops); + addIncludedOptions(map, vertexToFetch, loops); + applyTraversalRingToMap(map); + return (Map) map; + } + return super.map(traverser); + } + + private void addElementProperties(Map map, Vertex vertexToFetch, int loops){ + Iterator properties = propertiesStepBatchFetcher + .fetchData(getTraversal(), vertexToFetch, loops).iterator(); + while(properties.hasNext()) { + Property property = (Property)properties.next(); + String propertyKey = property.key(); + if(!prefetchAllPropertiesRequired || propertyKeysSet.isEmpty() || propertyKeysSet.contains(propertyKey)){ + Object value = this.returnType == PropertyType.VALUE ? property.value() : property; + map.compute(propertyKey, (k, v) -> { + final List values = v != null ? (List) v : new ArrayList<>(); + values.add(value); + return values; + }); + } + } + } + + private void addIncludedOptions(Map map, Vertex vertexToFetch, int loops){ + if (this.returnType == PropertyType.VALUE) { + if (withIdsFetching) { + map.put(T.id, getElementId(vertexToFetch)); + } + if (withLabelsFetching) { + map.put(T.label, labelStepBatchFetcher.fetchData(getTraversal(), vertexToFetch, loops)); + } + } + } + + @Override + public void setMetrics(MutableMetrics metrics) { + queryProfiler = new TP3ProfileWrapper(metrics); + } + + @Override + public void setUseMultiQuery(boolean useMultiQuery) { + this.useMultiQuery = useMultiQuery; + if(useMultiQuery && propertiesStepBatchFetcher == null){ + propertiesStepBatchFetcher = new PropertiesStepBatchFetcher(JanusGraphPropertyMapStep.this::makePropertiesQuery, batchSize); + } + createLabelFetcherIfNeeded(); + } + + private void createLabelFetcherIfNeeded(){ + if(useMultiQuery && withLabelsFetching && labelStepBatchFetcher == null){ + labelStepBatchFetcher = new LabelStepBatchFetcher(JanusGraphPropertyMapStep.this::makeLabelsQuery, batchSize); + } + } + + private Q makeLabelsQuery(Q query) { + return (Q) BasicVertexCentricQueryUtil.withLabelVertices((BasicVertexCentricQueryBuilder) query) + .profiler(queryProfiler); + } + + + private Q makePropertiesQuery(Q query) { + if(!prefetchAllPropertiesRequired){ + query.keys(getPropertyKeys()); + } + ((BasicVertexCentricQueryBuilder) query).profiler(queryProfiler); + return query; + } + + + @Override + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + if(labelStepBatchFetcher != null){ + labelStepBatchFetcher.setBatchSize(batchSize); + } + if(propertiesStepBatchFetcher != null){ + propertiesStepBatchFetcher.setBatchSize(batchSize); + } + } + + @Override + public void registerFirstNewLoopFutureVertexForPrefetching(Vertex futureVertex, int futureVertexTraverserLoop) { + if(useMultiQuery){ + if(withLabelsFetching){ + labelStepBatchFetcher.registerFirstNewLoopFutureVertexForPrefetching(futureVertex); + } + propertiesStepBatchFetcher.registerFirstNewLoopFutureVertexForPrefetching(futureVertex); + } + } + + @Override + public void registerSameLoopFutureVertexForPrefetching(Vertex futureVertex, int futureVertexTraverserLoop) { + if(useMultiQuery){ + if(withLabelsFetching) { + labelStepBatchFetcher.registerCurrentLoopFutureVertexForPrefetching(futureVertex, futureVertexTraverserLoop); + } + propertiesStepBatchFetcher.registerCurrentLoopFutureVertexForPrefetching(futureVertex, futureVertexTraverserLoop); + } + } + + @Override + public void registerNextLoopFutureVertexForPrefetching(Vertex futureVertex, int futureVertexTraverserLoop) { + if(useMultiQuery){ + if(withLabelsFetching) { + labelStepBatchFetcher.registerNextLoopFutureVertexForPrefetching(futureVertex, futureVertexTraverserLoop); + } + propertiesStepBatchFetcher.registerNextLoopFutureVertexForPrefetching(futureVertex, futureVertexTraverserLoop); + } + } + + public boolean isPrefetchAllPropertiesRequired() { + return prefetchAllPropertiesRequired; + } + + public void setPrefetchAllPropertiesRequired(boolean prefetchAllPropertiesRequired) { + this.prefetchAllPropertiesRequired = prefetchAllPropertiesRequired; + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/fetcher/LabelStepBatchFetcher.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/fetcher/LabelStepBatchFetcher.java new file mode 100644 index 00000000000..55533e6090c --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/step/fetcher/LabelStepBatchFetcher.java @@ -0,0 +1,48 @@ +// Copyright 2023 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.tinkerpop.optimize.step.fetcher; + +import com.google.common.collect.Iterables; +import org.janusgraph.core.JanusGraphMultiVertexQuery; +import org.janusgraph.core.JanusGraphVertex; +import org.janusgraph.graphdb.query.vertex.BasicVertexCentricQueryUtil; + +import java.util.HashMap; +import java.util.Map; + +public class LabelStepBatchFetcher extends MultiQueriableStepBatchFetcher{ + + private final FetchQueryBuildFunction fetchQueryBuildFunction; + + public LabelStepBatchFetcher(FetchQueryBuildFunction fetchQueryBuildFunction, int batchSize) { + super(batchSize); + this.fetchQueryBuildFunction = fetchQueryBuildFunction; + } + + @Override + protected Map makeQueryAndExecute(JanusGraphMultiVertexQuery multiQuery) { + multiQuery = fetchQueryBuildFunction.makeQuery(multiQuery); + Map> labelsBatch = multiQuery.vertices(); + Map result = new HashMap<>(labelsBatch.size()); + for(Map.Entry> labelEntry : labelsBatch.entrySet()){ + result.put( + labelEntry.getKey(), + BasicVertexCentricQueryUtil.castToVertexLabel(Iterables.getOnlyElement(labelEntry.getValue(),null)).name() + ); + } + return result; + } + +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AbstractJanusGraphMixedIndexAggStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AbstractJanusGraphMixedIndexAggStrategy.java index d4f69b40032..666a22baae3 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AbstractJanusGraphMixedIndexAggStrategy.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AbstractJanusGraphMixedIndexAggStrategy.java @@ -41,7 +41,7 @@ abstract class AbstractJanusGraphMixedIndexAggStrategy extends AbstractTraversal implements TraversalStrategy.ProviderOptimizationStrategy { private static final Set> POSTS = - Collections.singleton(JanusGraphLocalQueryOptimizerStrategy.class); + Collections.singleton(JanusGraphMultiQueriableCreationStrategy.class); @Override public void apply(final Traversal.Admin traversal) { diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AdjacentVertexHasUniquePropertyOptimizerStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AdjacentVertexHasUniquePropertyOptimizerStrategy.java index 390c520885b..f5f4a7163d6 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AdjacentVertexHasUniquePropertyOptimizerStrategy.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AdjacentVertexHasUniquePropertyOptimizerStrategy.java @@ -57,7 +57,7 @@ public class AdjacentVertexHasUniquePropertyOptimizerStrategy new AdjacentVertexHasUniquePropertyOptimizerStrategy(); private static final Set> POSTS = - new HashSet<>(Arrays.asList(JanusGraphStepStrategy.class, JanusGraphLocalQueryOptimizerStrategy.class)); + new HashSet<>(Arrays.asList(JanusGraphStepStrategy.class, JanusGraphMultiQueriableCreationStrategy.class)); private AdjacentVertexHasUniquePropertyOptimizerStrategy() {} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AdjacentVertexOptimizerStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AdjacentVertexOptimizerStrategy.java index 5f35f0b42bd..e77138aa978 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AdjacentVertexOptimizerStrategy.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/AdjacentVertexOptimizerStrategy.java @@ -45,7 +45,7 @@ protected enum OptimizablePosition { } private static final Set> POSTS = - Collections.singleton(JanusGraphLocalQueryOptimizerStrategy.class); + Collections.singleton(JanusGraphMultiQueriableCreationStrategy.class); @Override public Set> applyPost() { diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphLocalQueryOptimizerStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphLocalQueryOptimizerStrategy.java deleted file mode 100644 index 0dd5d6d7245..00000000000 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphLocalQueryOptimizerStrategy.java +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2017 JanusGraph Authors -// -// Licensed 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.janusgraph.graphdb.tinkerpop.optimize.strategy; - -import org.apache.tinkerpop.gremlin.process.traversal.Step; -import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin; -import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; -import org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalStep; -import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep; -import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep; -import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; -import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; -import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; -import org.janusgraph.graphdb.database.StandardJanusGraph; -import org.janusgraph.graphdb.query.QueryUtil; -import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphTraversalUtil; -import org.janusgraph.graphdb.tinkerpop.optimize.step.HasStepFolder; -import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphPropertiesStep; -import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphVertexStep; -import org.janusgraph.graphdb.tinkerpop.optimize.step.MultiQueriable; - -import java.util.Collections; -import java.util.Set; - -/** - * @author Marko A. Rodriguez (https://markorodriguez.com) - * @author Matthias Broecheler (http://matthiasb.com) - */ -public class JanusGraphLocalQueryOptimizerStrategy extends AbstractTraversalStrategy implements TraversalStrategy.ProviderOptimizationStrategy { - - private static final JanusGraphLocalQueryOptimizerStrategy INSTANCE = new JanusGraphLocalQueryOptimizerStrategy(); - - private JanusGraphLocalQueryOptimizerStrategy() { - } - - @Override - public void apply(final Traversal.Admin traversal) { - if (!traversal.getGraph().isPresent()) - return; - - final StandardJanusGraph janusGraph = JanusGraphTraversalUtil.getJanusGraph(traversal); - if (janusGraph == null) { - return; - } - - int txVertexCacheSize = janusGraph.getConfiguration().getTxVertexCacheSize(); - - applyJanusGraphVertexSteps(traversal); - applyJanusGraphPropertiesSteps(traversal, txVertexCacheSize); - inspectLocalTraversals(traversal, txVertexCacheSize); - } - - private void applyJanusGraphVertexSteps(Admin traversal) { - TraversalHelper.getStepsOfAssignableClass(VertexStep.class, traversal).forEach(originalStep -> { - final JanusGraphVertexStep vertexStep = new JanusGraphVertexStep(originalStep); - TraversalHelper.replaceStep(originalStep, vertexStep, originalStep.getTraversal()); - - if (JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep)) { - HasStepFolder.foldInHasContainer(vertexStep, originalStep.getTraversal(), originalStep.getTraversal()); - //We cannot fold in orders or ranges since they are not local - } - - assert JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep) || JanusGraphTraversalUtil.isVertexReturnStep(vertexStep); - final Step nextStep = JanusGraphTraversalUtil.getNextNonIdentityStep(vertexStep); - if (nextStep instanceof RangeGlobalStep) { - final int limit = QueryUtil.convertLimit(((RangeGlobalStep) nextStep).getHighRange()); - vertexStep.setLimit(0, QueryUtil.mergeHighLimits(limit, vertexStep.getHighLimit())); - } - }); - } - - private void applyJanusGraphPropertiesSteps(Admin traversal, int txVertexCacheSize) { - TraversalHelper.getStepsOfAssignableClass(PropertiesStep.class, traversal).forEach(originalStep -> { - final JanusGraphPropertiesStep propertiesStep = new JanusGraphPropertiesStep(originalStep); - propertiesStep.setBatchSize(txVertexCacheSize); - TraversalHelper.replaceStep(originalStep, propertiesStep, originalStep.getTraversal()); - - if (propertiesStep.getReturnType().forProperties()) { - HasStepFolder.foldInHasContainer(propertiesStep, originalStep.getTraversal(), originalStep.getTraversal()); - //We cannot fold in orders or ranges since they are not local - } - }); - } - - private void inspectLocalTraversals(final Admin traversal, int txVertexCacheSize) { - TraversalHelper.getStepsOfClass(LocalStep.class, traversal).forEach(localStep -> { - final Admin localTraversal = ((LocalStep) localStep).getLocalChildren().get(0); - final Step localStart = localTraversal.getStartStep(); - - if (localStart instanceof VertexStep) { - final JanusGraphVertexStep vertexStep = new JanusGraphVertexStep((VertexStep) localStart); - vertexStep.setBatchSize(txVertexCacheSize); - TraversalHelper.replaceStep(localStart, vertexStep, localTraversal); - - if (JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep)) { - HasStepFolder.foldInHasContainer(vertexStep, localTraversal, traversal); - HasStepFolder.foldInOrder(vertexStep, vertexStep.getNextStep(), localTraversal, traversal, false, null); - } - HasStepFolder.foldInRange(vertexStep, JanusGraphTraversalUtil.getNextNonIdentityStep(vertexStep), localTraversal, null); - - - unfoldLocalTraversal(traversal, localStep, localTraversal, vertexStep); - } else if (localStart instanceof PropertiesStep) { - final JanusGraphPropertiesStep propertiesStep = new JanusGraphPropertiesStep((PropertiesStep) localStart); - propertiesStep.setBatchSize(txVertexCacheSize); - - TraversalHelper.replaceStep(localStart, propertiesStep, localTraversal); - - if (propertiesStep.getReturnType().forProperties()) { - HasStepFolder.foldInHasContainer(propertiesStep, localTraversal, traversal); - HasStepFolder.foldInOrder(propertiesStep, propertiesStep.getNextStep(), localTraversal, traversal, false, null); - } - HasStepFolder.foldInRange(propertiesStep, JanusGraphTraversalUtil.getNextNonIdentityStep(propertiesStep), localTraversal, null); - - - unfoldLocalTraversal(traversal, localStep, localTraversal, propertiesStep); - } - - }); - } - - private static void unfoldLocalTraversal(final Traversal.Admin traversal, - LocalStep localStep, Traversal.Admin localTraversal, - MultiQueriable vertexStep) { - assert localTraversal.asAdmin().getSteps().size() > 0; - if (localTraversal.asAdmin().getSteps().size() == 1) { - //Can replace the entire localStep by the vertex step in the outer traversal - assert localTraversal.getStartStep() == vertexStep; - vertexStep.setTraversal(traversal); - TraversalHelper.replaceStep(localStep, vertexStep, traversal); - } - } - - private static final Set> PRIORS = Collections.singleton(AdjacentVertexFilterOptimizerStrategy.class); - - - @Override - public Set> applyPrior() { - return PRIORS; - } - - public static JanusGraphLocalQueryOptimizerStrategy instance() { - return INSTANCE; - } -} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphMultiQueriableCreationStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphMultiQueriableCreationStrategy.java new file mode 100644 index 00000000000..bc5a774887a --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphMultiQueriableCreationStrategy.java @@ -0,0 +1,208 @@ +// Copyright 2023 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.tinkerpop.optimize.strategy; + +import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.ElementMapStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertyMapStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; +import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; +import org.janusgraph.graphdb.database.StandardJanusGraph; +import org.janusgraph.graphdb.query.QueryUtil; +import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphMultiQueriableUtil; +import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphTraversalUtil; +import org.janusgraph.graphdb.tinkerpop.optimize.step.HasStepFolder; +import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphElementMapStep; +import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphHasStep; +import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphPropertiesStep; +import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphPropertyMapStep; +import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphVertexStep; +import org.janusgraph.graphdb.transaction.StandardJanusGraphTx; +import org.janusgraph.graphdb.transaction.TransactionConfiguration; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * Strategy which replaces all allowed steps with their `MultiQueriable` alternative steps. + */ +public class JanusGraphMultiQueriableCreationStrategy extends AbstractTraversalStrategy implements TraversalStrategy.ProviderOptimizationStrategy { + + private static final JanusGraphMultiQueriableCreationStrategy INSTANCE = new JanusGraphMultiQueriableCreationStrategy(); + + private static final Set> PRIORS = Collections.singleton(AdjacentVertexFilterOptimizerStrategy.class); + + private JanusGraphMultiQueriableCreationStrategy() { + } + + @Override + public void apply(final Traversal.Admin traversal) { + if (!traversal.getGraph().isPresent()) + return; + + final StandardJanusGraph janusGraph = JanusGraphTraversalUtil.getJanusGraph(traversal); + if (janusGraph == null) { + return; + } + + final Optional tx = JanusGraphTraversalUtil.getJanusGraphTx(traversal); + MultiQueryHasStepStrategyMode hasStepStrategyMode; + final MultiQueryPropertiesStrategyMode propertiesStrategyMode; + final int txVertexCacheSize; + final boolean hasPropertyPrefetching; + + if(tx.isPresent()){ + TransactionConfiguration txConfig = tx.get().getConfiguration(); + txVertexCacheSize = txConfig.getVertexCacheSize(); + hasPropertyPrefetching = txConfig.hasPropertyPrefetching(); + propertiesStrategyMode = txConfig.getPropertiesStrategyMode(); + hasStepStrategyMode = txConfig.getHasStepStrategyMode(); + } else { + GraphDatabaseConfiguration graphConfig = janusGraph.getConfiguration(); + txVertexCacheSize = graphConfig.getTxVertexCacheSize(); + hasPropertyPrefetching = graphConfig.hasPropertyPrefetching(); + propertiesStrategyMode = graphConfig.propertiesStrategyMode(); + hasStepStrategyMode = graphConfig.hasStepStrategyMode(); + } + + if(hasPropertyPrefetching && !MultiQueryHasStepStrategyMode.NONE.equals(hasStepStrategyMode)){ + // if `hasPropertyPrefetching` is `true` than modes which don't fetch all properties are redundant because + // the cached slice queries for separate properties are not going to be used for a query which accesses all properties together. + // Thus, having anything other than `ALL_PROPERTIES` - mean additional redundant requests. + // That's why we use `MultiQueryHasStepStrategyMode.ALL_PROPERTIES` whenever `hasPropertyPrefetching` is `true`. + hasStepStrategyMode = MultiQueryHasStepStrategyMode.ALL_PROPERTIES; + } + + // Process all steps of the current traversal + List steps = new ArrayList<>(traversal.getSteps()); + for(Step step : steps){ + if(step instanceof VertexStep){ + applyJanusGraphVertexStep((VertexStep) step, txVertexCacheSize); + } else if(step instanceof HasStep){ + if(!MultiQueryHasStepStrategyMode.NONE.equals(hasStepStrategyMode)){ + applyJanusGraphHasSteps((HasStep) step, txVertexCacheSize, hasStepStrategyMode); + } + } else if(step instanceof PropertiesStep){ + if(!MultiQueryPropertiesStrategyMode.NONE.equals(propertiesStrategyMode)){ + applyJanusGraphPropertiesStep((PropertiesStep) step, txVertexCacheSize, propertiesStrategyMode); + } + } else if(step instanceof PropertyMapStep){ + if(!MultiQueryPropertiesStrategyMode.NONE.equals(propertiesStrategyMode)){ + applyJanusGraphPropertyMapStep((PropertyMapStep) step, txVertexCacheSize, propertiesStrategyMode); + } + } else if(step instanceof ElementMapStep){ + if(!MultiQueryPropertiesStrategyMode.NONE.equals(propertiesStrategyMode)){ + applyJanusGraphElementMapStep((ElementMapStep) step, txVertexCacheSize, propertiesStrategyMode); + } + } else if(step instanceof LocalStep){ + JanusGraphMultiQueriableUtil.updateStartOfLocalStepToMultiQueriable(traversal, (LocalStep) step, txVertexCacheSize); + } + } + } + + private void applyJanusGraphVertexStep(VertexStep originalStep, int txVertexCacheSize) { + if(originalStep instanceof JanusGraphVertexStep){ + return; + } + final JanusGraphVertexStep vertexStep = new JanusGraphVertexStep(originalStep); + vertexStep.setBatchSize(txVertexCacheSize); + TraversalHelper.replaceStep(originalStep, vertexStep, originalStep.getTraversal()); + + if (JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep)) { + HasStepFolder.foldInHasContainer(vertexStep, originalStep.getTraversal(), originalStep.getTraversal()); + //We cannot fold in orders or ranges since they are not local + } + + assert JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep) || JanusGraphTraversalUtil.isVertexReturnStep(vertexStep); + final Step nextStep = JanusGraphTraversalUtil.getNextNonIdentityStep(vertexStep); + if (nextStep instanceof RangeGlobalStep) { + final int limit = QueryUtil.convertLimit(((RangeGlobalStep) nextStep).getHighRange()); + vertexStep.setLimit(0, QueryUtil.mergeHighLimits(limit, vertexStep.getHighLimit())); + } + } + + private void applyJanusGraphHasSteps(final HasStep originalStep, final int txVertexCacheSize, + final MultiQueryHasStepStrategyMode hasStepStrategyMode) { + if(originalStep instanceof JanusGraphHasStep){ + return; + } + final JanusGraphHasStep janusGraphHasStep = JanusGraphMultiQueriableUtil.createJanusGraphHasStep(originalStep, txVertexCacheSize, hasStepStrategyMode); + TraversalHelper.replaceStep(originalStep, janusGraphHasStep, originalStep.getTraversal()); + } + + private void applyJanusGraphPropertiesStep(PropertiesStep originalStep, int txVertexCacheSize, + MultiQueryPropertiesStrategyMode propertiesStrategyMode) { + if(originalStep instanceof JanusGraphPropertiesStep){ + return; + } + final JanusGraphPropertiesStep propertiesStep = new JanusGraphPropertiesStep(originalStep); + propertiesStep.setBatchSize(txVertexCacheSize); + if(MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.equals(propertiesStrategyMode)){ + propertiesStep.setPrefetchAllPropertiesRequired(true); + } + TraversalHelper.replaceStep(originalStep, propertiesStep, originalStep.getTraversal()); + + if (propertiesStep.getReturnType().forProperties()) { + HasStepFolder.foldInHasContainer(propertiesStep, originalStep.getTraversal(), originalStep.getTraversal()); + //We cannot fold in orders or ranges since they are not local + } + } + + private void applyJanusGraphPropertyMapStep(PropertyMapStep originalStep, int txVertexCacheSize, + MultiQueryPropertiesStrategyMode propertiesStrategyMode) { + if(originalStep instanceof JanusGraphPropertyMapStep){ + return; + } + final JanusGraphPropertyMapStep propertyMapStep = new JanusGraphPropertyMapStep(originalStep); + propertyMapStep.setBatchSize(txVertexCacheSize); + if(MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.equals(propertiesStrategyMode)){ + propertyMapStep.setPrefetchAllPropertiesRequired(true); + } + TraversalHelper.replaceStep(originalStep, propertyMapStep, originalStep.getTraversal()); + } + + private void applyJanusGraphElementMapStep(ElementMapStep originalStep, int txVertexCacheSize, + MultiQueryPropertiesStrategyMode propertiesStrategyMode) { + if(originalStep instanceof JanusGraphElementMapStep){ + return; + } + final JanusGraphElementMapStep elementMapStep = new JanusGraphElementMapStep(originalStep); + elementMapStep.setBatchSize(txVertexCacheSize); + if(MultiQueryPropertiesStrategyMode.ALL_PROPERTIES.equals(propertiesStrategyMode)){ + elementMapStep.setPrefetchAllPropertiesRequired(true); + } + TraversalHelper.replaceStep(originalStep, elementMapStep, originalStep.getTraversal()); + } + + @Override + public Set> applyPrior() { + return PRIORS; + } + + public static JanusGraphMultiQueriableCreationStrategy instance() { + return INSTANCE; + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphMultiQueryStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphMultiQueryStrategy.java index 007a6e31d2d..f3d38b6385b 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphMultiQueryStrategy.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/JanusGraphMultiQueryStrategy.java @@ -43,7 +43,7 @@ */ public class JanusGraphMultiQueryStrategy extends AbstractTraversalStrategy implements TraversalStrategy.ProviderOptimizationStrategy { - private static final Set> PRIORS = new HashSet<>(Arrays.asList(JanusGraphLocalQueryOptimizerStrategy.class, JanusGraphStepStrategy.class, JanusGraphHasStepStrategy.class)); + private static final Set> PRIORS = new HashSet<>(Arrays.asList(JanusGraphMultiQueriableCreationStrategy.class, JanusGraphStepStrategy.class)); private static final JanusGraphMultiQueryStrategy INSTANCE = new JanusGraphMultiQueryStrategy(); private static final MultiQueriableStepRegistrationConsumer ATTACH_FIRST_LOOP = JanusGraphMultiQueryStep::attachFirstLoopClient; diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/MultiQueryPropertiesStrategyMode.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/MultiQueryPropertiesStrategyMode.java new file mode 100644 index 00000000000..ddc407fd3b5 --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/strategy/MultiQueryPropertiesStrategyMode.java @@ -0,0 +1,47 @@ +// Copyright 2023 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.tinkerpop.optimize.strategy; + +import org.janusgraph.graphdb.configuration.ConfigName; + +public enum MultiQueryPropertiesStrategyMode implements ConfigName { + + /** + * Prefetch all properties on any property access. + */ + ALL_PROPERTIES("all_properties"), + + /** + * Prefetch needed properties only. + */ + REQUIRED_PROPERTIES_ONLY("required_properties_only"), + + /** + * Skips properties pre-fetch optimization. + */ + NONE("none") + ; + + private final String configurationOptionName; + + MultiQueryPropertiesStrategyMode(String configurationOptionName){ + this.configurationOptionName = configurationOptionName; + } + + @Override + public String getConfigName() { + return configurationOptionName; + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/StandardTransactionBuilder.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/StandardTransactionBuilder.java index d67d4f9f9be..787c71d103e 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/StandardTransactionBuilder.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/StandardTransactionBuilder.java @@ -29,6 +29,7 @@ import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; import org.janusgraph.graphdb.database.StandardJanusGraph; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryHasStepStrategyMode; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryPropertiesStrategyMode; import java.time.Instant; @@ -88,6 +89,8 @@ public class StandardTransactionBuilder implements TransactionConfiguration, Tra private MultiQueryHasStepStrategyMode hasStepStrategyMode; + private MultiQueryPropertiesStrategyMode propertiesStrategyMode; + private final boolean forceIndexUsage; private final ModifiableConfiguration writableCustomOptions; @@ -111,6 +114,7 @@ private StandardTransactionBuilder(GraphDatabaseConfiguration graphConfig, Stand this.propertyPrefetching = graphConfig.hasPropertyPrefetching(); this.multiQuery = graphConfig.useMultiQuery(); this.hasStepStrategyMode = graphConfig.hasStepStrategyMode(); + this.propertiesStrategyMode = graphConfig.propertiesStrategyMode(); this.writableCustomOptions = writableCustomOptions; if(customOptions == null){ this.customOptions = new MergedConfiguration(writableCustomOptions, graphConfig.getConfiguration()); @@ -233,6 +237,12 @@ public TransactionBuilder setHasStepStrategyMode(MultiQueryHasStepStrategyMode h return this; } + @Override + public TransactionBuilder setPropertiesStrategyMode(MultiQueryPropertiesStrategyMode propertiesStrategyMode) { + this.propertiesStrategyMode = propertiesStrategyMode; + return this; + } + @Override public void setCommitTime(Instant time) { throw new UnsupportedOperationException("Use setCommitTime(long,TimeUnit)"); @@ -279,7 +289,8 @@ public JanusGraphTransaction start() { propertyPrefetching, multiQuery, singleThreaded, threadBound, getTimestampProvider(), userCommitTime, indexCacheWeight, getVertexCacheSize(), getDirtyVertexSize(), logIdentifier, restrictedPartitions, groupName, - defaultSchemaMaker, hasDisabledSchemaConstraints, skipDBCacheRead, hasStepStrategyMode, customOptions); + defaultSchemaMaker, hasDisabledSchemaConstraints, skipDBCacheRead, hasStepStrategyMode, propertiesStrategyMode, + customOptions); return graph.newTransaction(immutable); } @@ -401,6 +412,11 @@ public MultiQueryHasStepStrategyMode getHasStepStrategyMode() { return hasStepStrategyMode; } + @Override + public MultiQueryPropertiesStrategyMode getPropertiesStrategyMode() { + return propertiesStrategyMode; + } + @Override public String getGroupName() { return groupName; @@ -461,27 +477,29 @@ private static class ImmutableTxCfg implements TransactionConfiguration { private final DefaultSchemaMaker defaultSchemaMaker; private boolean hasDisabledSchemaConstraints = true; private MultiQueryHasStepStrategyMode hasStepStrategyMode; + private MultiQueryPropertiesStrategyMode propertiesStrategyMode; private final BaseTransactionConfig handleConfig; public ImmutableTxCfg(boolean isReadOnly, - boolean hasEnabledBatchLoading, - boolean hasAssignIDsImmediately, - boolean hasPreloadedData, - boolean hasForceIndexUsage, - boolean hasVerifyExternalVertexExistence, - boolean hasVerifyInternalVertexExistence, - boolean hasAcquireLocks, boolean hasVerifyUniqueness, - boolean hasPropertyPrefetching, boolean useMultiQuery, boolean isSingleThreaded, - boolean isThreadBound, TimestampProvider times, Instant commitTime, - long indexCacheWeight, int vertexCacheSize, int dirtyVertexSize, String logIdentifier, - int[] restrictedPartitions, - String groupName, - DefaultSchemaMaker defaultSchemaMaker, - boolean hasDisabledSchemaConstraints, - boolean skipDBCacheRead, - MultiQueryHasStepStrategyMode hasStepStrategyMode, - Configuration customOptions) { + boolean hasEnabledBatchLoading, + boolean hasAssignIDsImmediately, + boolean hasPreloadedData, + boolean hasForceIndexUsage, + boolean hasVerifyExternalVertexExistence, + boolean hasVerifyInternalVertexExistence, + boolean hasAcquireLocks, boolean hasVerifyUniqueness, + boolean hasPropertyPrefetching, boolean useMultiQuery, boolean isSingleThreaded, + boolean isThreadBound, TimestampProvider times, Instant commitTime, + long indexCacheWeight, int vertexCacheSize, int dirtyVertexSize, String logIdentifier, + int[] restrictedPartitions, + String groupName, + DefaultSchemaMaker defaultSchemaMaker, + boolean hasDisabledSchemaConstraints, + boolean skipDBCacheRead, + MultiQueryHasStepStrategyMode hasStepStrategyMode, + MultiQueryPropertiesStrategyMode propertiesStrategyMode, + Configuration customOptions) { this.isReadOnly = isReadOnly; this.hasEnabledBatchLoading = hasEnabledBatchLoading; this.hasAssignIDsImmediately = hasAssignIDsImmediately; @@ -504,6 +522,7 @@ public ImmutableTxCfg(boolean isReadOnly, this.hasDisabledSchemaConstraints = hasDisabledSchemaConstraints; this.skipDBCacheRead = skipDBCacheRead; this.hasStepStrategyMode = hasStepStrategyMode; + this.propertiesStrategyMode = propertiesStrategyMode; this.handleConfig = new StandardBaseTransactionConfig.Builder() .commitTime(commitTime) .timestampProvider(times) @@ -626,6 +645,11 @@ public MultiQueryHasStepStrategyMode getHasStepStrategyMode() { return hasStepStrategyMode; } + @Override + public MultiQueryPropertiesStrategyMode getPropertiesStrategyMode() { + return propertiesStrategyMode; + } + @Override public Instant getCommitTime() { return handleConfig.getCommitTime(); diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/TransactionConfiguration.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/TransactionConfiguration.java index 72827e04f03..1b582c119d3 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/TransactionConfiguration.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/TransactionConfiguration.java @@ -17,6 +17,7 @@ import org.janusgraph.core.schema.DefaultSchemaMaker; import org.janusgraph.diskstorage.BaseTransactionConfig; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryHasStepStrategyMode; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.MultiQueryPropertiesStrategyMode; /** * Provides configuration options for {@link org.janusgraph.core.JanusGraphTransaction}. @@ -198,8 +199,13 @@ public interface TransactionConfiguration extends BaseTransactionConfig { boolean isSkipDBCacheRead(); /** - * @return Has step strategy mode used for the transaction. Can be configured via config `query.has-step-batch-mode`. + * @return Has step strategy mode used for the transaction. Can be configured via config `query.batch.has-step-mode`. */ MultiQueryHasStepStrategyMode getHasStepStrategyMode(); + /** + * @return Properties strategy mode used for the transaction. Can be configured via config `query.batch.properties-mode`. + */ + MultiQueryPropertiesStrategyMode getPropertiesStrategyMode(); + } diff --git a/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphLocalQueryOptimizerStrategyTest.java b/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueriableCreationStrategyTest.java similarity index 99% rename from janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphLocalQueryOptimizerStrategyTest.java rename to janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueriableCreationStrategyTest.java index 86b2443a8da..6d85396a96a 100644 --- a/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphLocalQueryOptimizerStrategyTest.java +++ b/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueriableCreationStrategyTest.java @@ -41,7 +41,7 @@ /** * @author Florian Grieskamp (Florian.Grieskamp@gdata.de) */ -public class JanusGraphLocalQueryOptimizerStrategyTest extends OptimizerStrategyTest { +public class JanusGraphMultiQueriableCreationStrategyTest extends OptimizerStrategyTest { @Test public void testDefaultConfiguration() { diff --git a/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueryStrategyTest.java b/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueryStrategyTest.java index 69575622ef7..eb7be0ddfb8 100644 --- a/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueryStrategyTest.java +++ b/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphMultiQueryStrategyTest.java @@ -34,7 +34,7 @@ import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphPropertiesStep; import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphStep; import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphVertexStep; -import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphLocalQueryOptimizerStrategy; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphMultiQueriableCreationStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphUnusedMultiQueryRemovalStrategy; import org.junit.jupiter.api.Test; @@ -54,12 +54,12 @@ public class JanusGraphMultiQueryStrategyTest extends OptimizerStrategyTest { @Test - public void testQueryIsExecutableIfJanusGraphLocalQueryOptimizerStrategyIsEnabled() { + public void testQueryIsExecutableIfJanusGraphMultiQueriableReplacementStrategyIsEnabled() { clopen(option(USE_MULTIQUERY), true); makeSampleGraph(); final List normalResults = g.V(sv[0]).outE().inV().choose(__.inE("knows").has("weight", 0), __.inE("knows").has("weight", 1), __.inE("knows").has("weight", 2)).toList(); - final List resultsWithDisabledStrategy = g.withoutStrategies(JanusGraphLocalQueryOptimizerStrategy.class).V(sv[0]).outE().inV().choose(__.inE("knows").has("weight", 0), __.inE("knows").has("weight", 1), __.inE("knows").has("weight", 2)).toList(); + final List resultsWithDisabledStrategy = g.withoutStrategies(JanusGraphMultiQueriableCreationStrategy.class).V(sv[0]).outE().inV().choose(__.inE("knows").has("weight", 0), __.inE("knows").has("weight", 1), __.inE("knows").has("weight", 2)).toList(); assertEquals(normalResults, resultsWithDisabledStrategy); } diff --git a/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStepStrategyTest.java b/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStepStrategyTest.java index cd5f2c78bd6..741acc01e8a 100644 --- a/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStepStrategyTest.java +++ b/janusgraph-test/src/test/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStepStrategyTest.java @@ -57,8 +57,7 @@ import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphPropertiesStep; import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphStep; import org.janusgraph.graphdb.tinkerpop.optimize.step.JanusGraphVertexStep; -import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphHasStepStrategy; -import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphLocalQueryOptimizerStrategy; +import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphMultiQueriableCreationStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphMultiQueryStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphStepStrategy; import org.janusgraph.graphdb.tinkerpop.optimize.strategy.JanusGraphUnusedMultiQueryRemovalStrategy; @@ -334,8 +333,7 @@ private static Stream generateMultiQueryTestParameters() { final String MQ_STEP = JanusGraphMultiQueryStep.class.getSimpleName(); List otherStrategies = new ArrayList<>(2); - otherStrategies.add(JanusGraphLocalQueryOptimizerStrategy.instance()); - otherStrategies.add(JanusGraphHasStepStrategy.instance()); + otherStrategies.add(JanusGraphMultiQueriableCreationStrategy.instance()); otherStrategies.add(JanusGraphMultiQueryStrategy.instance()); otherStrategies.add(JanusGraphUnusedMultiQueryRemovalStrategy.instance()); int defaultBarrierSize = GraphDatabaseConfiguration.LIMITED_BATCH_SIZE.getDefaultValue();