This is an automated email from the ASF dual-hosted git repository. Cole-Greer pushed a commit to branch 3.8-dev in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit fe2abf26280e56e45872d4e17fd73baef41fa4c2 Merge: e8f41c81b9 5349dbb89d Author: Cole Greer <[email protected]> AuthorDate: Wed Jul 22 17:02:16 2026 -0700 Merge branch '3.7-dev' into 3.8-dev CHANGELOG.asciidoc | 5 ++++ docs/src/upgrade/release-3.7.x.asciidoc | 9 +++++++ .../optimization/ProductiveByStrategy.java | 3 +++ .../Strategy/Optimization/ProductiveByStrategy.cs | 2 ++ gremlin-go/driver/strategies.go | 7 ++++- gremlin-go/driver/strategies_test.go | 31 ++++++++++++++++++++++ .../lib/process/traversal-strategy.js | 4 +++ .../python/gremlin_python/process/strategies.py | 7 +++++ .../python/gremlin_python/process/traversal.py | 2 +- .../python/tests/unit/process/test_strategies.py | 12 +++++++++ 10 files changed, 80 insertions(+), 2 deletions(-) diff --cc gremlin-go/driver/strategies.go index 076b22f14b,f03a00f01f..88c1785f12 --- a/gremlin-go/driver/strategies.go +++ b/gremlin-go/driver/strategies.go @@@ -420,11 -364,12 +420,14 @@@ func PathRetractionStrategy() Traversal // the initial Traversal argument or null. In this way, the By() is always "productive". This strategy // is an "optimization" but it is perhaps more of a "decoration", but it should follow // ByModulatorOptimizationStrategy which features optimizations relevant to this one. + // + // Deprecated: As of release 3.7.7, not replaced. This strategy was added as a temporary way to mimic + // pre-3.5.0 null processing behavior. -func ProductiveByStrategy(config ProductiveByStrategyConfig) TraversalStrategy { +func ProductiveByStrategy(config ...ProductiveByStrategyConfig) TraversalStrategy { configMap := make(map[string]interface{}) - configMap["productiveKeys"] = config.ProductiveKeys + if len(config) > 0 { + configMap["productiveKeys"] = config[0].ProductiveKeys + } return &traversalStrategy{name: optimizationNamespace + "ProductiveByStrategy", configuration: configMap} } diff --cc gremlin-go/driver/strategies_test.go index 4946a74303,395b50fd71..d09e59add2 --- a/gremlin-go/driver/strategies_test.go +++ b/gremlin-go/driver/strategies_test.go @@@ -413,18 -413,35 +413,49 @@@ func TestStrategy(t *testing.T) assert.Nil(t, err) assert.Equal(t, int32(6), val) }) + + t.Run("Test without strategies MessagePassingReductionStrategy", func(t *testing.T) { + g := getModernGraph(t, testNoAuthUrl, &AuthInfo{}, &tls.Config{}) + defer g.remoteConnection.Close() + + count, err := g.WithoutStrategies(MessagePassingReductionStrategy()).V().Count().ToList() + assert.Nil(t, err) + assert.NotNil(t, count) + assert.Equal(t, 1, len(count)) + val, err := count[0].GetInt32() + assert.Nil(t, err) + assert.Equal(t, int32(6), val) + }) + } + + func Test_StrategyConfig_partitionNilReadPartitions(t *testing.T) { + t.Run("Test PartitionStrategy with nil ReadPartitions does not panic", func(t *testing.T) { + config := PartitionStrategyConfig{ + PartitionKey: "partition", + WritePartition: "write", + // ReadPartitions intentionally left unset (nil interface). + } + var strategy TraversalStrategy + assert.NotPanics(t, func() { + strategy = PartitionStrategy(config) + }) + assert.NotNil(t, strategy) + configMap := strategy.(*traversalStrategy).configuration + _, ok := configMap["readPartitions"] + assert.False(t, ok) + }) + + t.Run("Test PartitionStrategy with non-empty ReadPartitions sets readPartitions", func(t *testing.T) { + config := PartitionStrategyConfig{ + PartitionKey: "partition", + WritePartition: "write", + ReadPartitions: NewSimpleSet("read"), + } + strategy := PartitionStrategy(config) + assert.NotNil(t, strategy) + configMap := strategy.(*traversalStrategy).configuration + _, ok := configMap["readPartitions"] + assert.True(t, ok) + }) + } diff --cc gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js index 51e992716f,f673d46d58..b83acfba6f --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js @@@ -276,17 -243,6 +276,21 @@@ class PathRetractionStrategy extends Tr } } ++/** ++ * @deprecated As of release 3.7.7, not replaced. This strategy was added as a temporary way to mimic pre-3.5.0 ++ * null processing behavior. ++ */ +class ProductiveByStrategy extends TraversalStrategy { + /** + * @param {Object} [options] + * @param {Array<String>} productiveKeys set of keys that will always be productive + */ + constructor({ productiveKeys = [] } = {}) { + super('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy'); + this.configuration['productiveKeys'] = productiveKeys; + } +} + class CountStrategy extends TraversalStrategy { constructor() { super('org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy'); diff --cc gremlin-python/src/main/python/gremlin_python/process/strategies.py index 6aceb61edd,5ebbe98405..5dc7889d79 --- a/gremlin-python/src/main/python/gremlin_python/process/strategies.py +++ b/gremlin-python/src/main/python/gremlin_python/process/strategies.py @@@ -19,7 -19,8 +19,8 @@@ __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' -from gremlin_python.process.traversal import TraversalStrategy +from .traversal import TraversalStrategy + import warnings base_namespace = 'org.apache.tinkerpop.gremlin.process.traversal.strategy.' decoration_namespace = base_namespace + 'decoration.' diff --cc gremlin-python/src/main/python/tests/unit/process/test_strategies.py index adf8bcd586,1f8846e833..05d565e941 --- a/gremlin-python/src/main/python/tests/unit/process/test_strategies.py +++ b/gremlin-python/src/main/python/tests/unit/process/test_strategies.py @@@ -110,7 -110,19 +110,19 @@@ class TestTraversalStrategies(object) assert 1 == len(strategy.configuration) assert __.has("name","marko") == strategy.configuration["vertices"] ### + bytecode = g.withStrategies(ProductiveByStrategy(productiveKeys=["name", "age"])).bytecode + assert 1 == len(bytecode.source_instructions) + assert 2 == len(bytecode.source_instructions[0]) + assert "withStrategies" == bytecode.source_instructions[0][0] + assert ProductiveByStrategy() == bytecode.source_instructions[0][1] + strategy = bytecode.source_instructions[0][1] + assert 1 == len(strategy.configuration) + assert ["name", "age"] == strategy.configuration["productiveKeys"] + ### + bytecode = g.withStrategies(ProductiveByStrategy()).bytecode + assert 0 == len(bytecode.source_instructions[0][1].configuration) + ### - bytecode = g.withStrategies(OptionsStrategy(options={"x": "test", "y": True})).bytecode + bytecode = g.withStrategies(OptionsStrategy(x="test", y=True)).bytecode assert 1 == len(bytecode.source_instructions) assert 2 == len(bytecode.source_instructions[0]) assert "withStrategies" == bytecode.source_instructions[0][0]
