From 4bebd56cb509ac8a2450735ffa8dc226b8c3426f Mon Sep 17 00:00:00 2001 From: agrgr Date: Sun, 7 Dec 2025 11:44:37 +0100 Subject: [PATCH] support selecting secondary index by its name --- .../java/com/aerospike/dsl/IndexContext.java | 22 ++++++++++++ .../LogicalParsedExpressionTests.java | 35 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/main/java/com/aerospike/dsl/IndexContext.java b/src/main/java/com/aerospike/dsl/IndexContext.java index d655963..ad590f2 100644 --- a/src/main/java/com/aerospike/dsl/IndexContext.java +++ b/src/main/java/com/aerospike/dsl/IndexContext.java @@ -1,9 +1,11 @@ package com.aerospike.dsl; +import com.aerospike.dsl.client.query.Filter; import lombok.AllArgsConstructor; import lombok.Getter; import java.util.Collection; +import java.util.List; /** * This class stores namespace and indexes required to build secondary index Filter @@ -22,4 +24,24 @@ public class IndexContext { * with bins in DSL String */ private Collection indexes; + + + /** + * Create index context specifying the index to be used. + * + * @param namespace Namespace to be used for creating {@link Filter}. Is matched with namespace of indexes + * @param indexes Collection of {@link Index} objects to be used for creating {@link Filter}. Bin name and + * index type are matched with bins in DSL String + * @param indexToUse The name of an index to use for creating {@link Filter}. If the index with the specified name + * is not found, the resulting index is chosen the usual way (cardinality-based or alphabetically) + * @return A new instance of {@code IndexContext} + */ + public static IndexContext of(String namespace, Collection indexes, String indexToUse) { + List singleIndexList = indexes.stream().filter(idx -> areNamesEqual(idx, indexToUse)).toList(); + return new IndexContext(namespace, singleIndexList.isEmpty() ? indexes : singleIndexList); + } + + private static boolean areNamesEqual(Index idx, String indexToUse) { + return idx != null && idx.getName() != null && idx.getName().equals(indexToUse); + } } diff --git a/src/test/java/com/aerospike/dsl/parsedExpression/LogicalParsedExpressionTests.java b/src/test/java/com/aerospike/dsl/parsedExpression/LogicalParsedExpressionTests.java index bdd6585..e76dc4a 100644 --- a/src/test/java/com/aerospike/dsl/parsedExpression/LogicalParsedExpressionTests.java +++ b/src/test/java/com/aerospike/dsl/parsedExpression/LogicalParsedExpressionTests.java @@ -153,6 +153,41 @@ void binLogical_AND_AND_all_indexes_no_cardinality() { IndexContext.of(TestUtils.NAMESPACE, indexes)); } + @Test + void binLogical_AND_AND_explicitly_given_index() { + List indexes = List.of( + Index.builder().namespace(TestUtils.NAMESPACE).name("idx_bin1").bin("intBin1") + .indexType(IndexType.NUMERIC).binValuesRatio(0).build(), + Index.builder().namespace(TestUtils.NAMESPACE).name("idx_bin2").bin("intBin2") + .indexType(IndexType.NUMERIC).binValuesRatio(1).build(), + Index.builder().namespace(TestUtils.NAMESPACE).name("idx_bin3").bin("intBin3") + .indexType(IndexType.NUMERIC).binValuesRatio(0).build() + ); + Filter filter = Filter.range("intBin1", 101, Long.MAX_VALUE); // The index has been chosen manually + Exp exp = Exp.and( + Exp.gt(Exp.intBin("intBin2"), Exp.val(100)), + Exp.gt(Exp.intBin("intBin3"), Exp.val(100)) + ); + parseDslExpressionAndCompare(ExpressionContext.of("$.intBin1 > 100 and $.intBin2 > 100 and $.intBin3 > 100"), filter, exp, + IndexContext.of(TestUtils.NAMESPACE, indexes, "idx_bin1")); // Manually selecting the index by its name + } + + @Test + void binLogical_AND_AND_explicitly_given_index_unavailable() { + List indexes = List.of( + Index.builder().namespace(TestUtils.NAMESPACE).bin("intBin1").indexType(IndexType.NUMERIC).binValuesRatio(0).build(), + Index.builder().namespace(TestUtils.NAMESPACE).name("idx_bin2").bin("intBin2").indexType(IndexType.NUMERIC).binValuesRatio(1).build(), + Index.builder().namespace(TestUtils.NAMESPACE).bin("intBin3").indexType(IndexType.NUMERIC).binValuesRatio(0).build() + ); + Filter filter = Filter.range("intBin2", 101, Long.MAX_VALUE); // Fallback to choosing the index regularly + Exp exp = Exp.and( + Exp.gt(Exp.intBin("intBin1"), Exp.val(100)), + Exp.gt(Exp.intBin("intBin3"), Exp.val(100)) + ); + parseDslExpressionAndCompare(ExpressionContext.of("$.intBin1 > 100 and $.intBin2 > 100 and $.intBin3 > 100"), filter, exp, + IndexContext.of(TestUtils.NAMESPACE, indexes, "intBin10")); // There is no index with the name "intBin10" + } + @Test void binLogical_AND_AND_all_indexes_partial_data() { List indexes = List.of(