This is an automated email from the ASF dual-hosted git repository.
absurdfarce pushed a commit to branch 4.x
in repository https://gitbox.apache.org/repos/asf/cassandra-java-driver.git
The following commit(s) were added to refs/heads/4.x by this push:
new af0a63a91 Re-work ordering clause support in query builder behind a
single OrderingClause abstraction. This allows for custom impls to define
their own logic for managing "ORDER BY" clauses.
af0a63a91 is described below
commit af0a63a91a3dd1d3585070258e5c711fff50d77b
Author: absurdfarce <[email protected]>
AuthorDate: Mon Jul 21 16:11:32 2025 -0500
Re-work ordering clause support in query builder behind a single
OrderingClause abstraction. This allows for
custom impls to define their own logic for managing "ORDER BY" clauses.
patch by Bret McGuire; reviewed by Bret McGuire and Lukasz Antoniak
reference: https://github.com/apache/cassandra-java-driver/pull/2047
---
.../api/querybuilder/select/AnnOrderingClause.java | 50 ++++++++
.../querybuilder/select/ColumnsOrderingClause.java | 68 +++++++++++
.../api/querybuilder/select/OrderingClause.java | 27 +++++
.../querybuilder/select/DefaultSelect.java | 133 +++++++--------------
.../querybuilder/select/SelectOrderingTest.java | 27 +++--
5 files changed, 208 insertions(+), 97 deletions(-)
diff --git
a/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/AnnOrderingClause.java
b/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/AnnOrderingClause.java
new file mode 100644
index 000000000..a2226d88a
--- /dev/null
+++
b/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/AnnOrderingClause.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.datastax.oss.driver.api.querybuilder.select;
+
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.data.CqlVector;
+import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * Concrete implementation of {@link OrderingClause} which supports ordering by
+ * the adjacent nearest-neighbor (ANN) calculation. This usage is primarily
used
+ * for vector calculations.
+ */
+public class AnnOrderingClause extends OrderingClause {
+
+ private final CqlIdentifier identifier;
+ private final CqlVector<?> vector;
+
+ AnnOrderingClause(CqlIdentifier identifier, CqlVector<?> vector) {
+
+ this.identifier = identifier;
+ this.vector = vector;
+ }
+
+ public static AnnOrderingClause create(CqlIdentifier identifier,
CqlVector<?> vector) {
+ return new AnnOrderingClause(identifier, vector);
+ }
+
+ @Override
+ public void appendTo(@NonNull StringBuilder builder) {
+ builder.append(" ORDER BY ").append(this.identifier.asCql(true)).append("
ANN OF ");
+ QueryBuilder.literal(this.vector).appendTo(builder);
+ }
+}
diff --git
a/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/ColumnsOrderingClause.java
b/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/ColumnsOrderingClause.java
new file mode 100644
index 000000000..b33a9d8e8
--- /dev/null
+++
b/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/ColumnsOrderingClause.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.datastax.oss.driver.api.querybuilder.select;
+
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder;
+import com.datastax.oss.driver.internal.querybuilder.ImmutableCollections;
+import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.util.Map;
+
+/**
+ * Concrete implementation of {@link OrderingClause} which supports ordering by
+ * specified columns. This usages is the default ORDER BY syntax for Apache
Cassandra.
+ */
+public class ColumnsOrderingClause extends OrderingClause {
+
+ private final ImmutableMap<CqlIdentifier, ClusteringOrder> orderings;
+
+ ColumnsOrderingClause(ImmutableMap<CqlIdentifier, ClusteringOrder>
orderings) {
+
+ this.orderings = orderings;
+ }
+
+ public static ColumnsOrderingClause create() {
+ return new ColumnsOrderingClause(ImmutableMap.of());
+ }
+
+ public ColumnsOrderingClause add(
+ @NonNull CqlIdentifier identifier, @NonNull ClusteringOrder order) {
+ return new ColumnsOrderingClause(
+ ImmutableCollections.append(this.orderings, identifier, order));
+ }
+
+ public ColumnsOrderingClause add(@NonNull Map<CqlIdentifier,
ClusteringOrder> orderMap) {
+ return new
ColumnsOrderingClause(ImmutableCollections.concat(this.orderings, orderMap));
+ }
+
+ @Override
+ public void appendTo(@NonNull StringBuilder builder) {
+
+ boolean first = true;
+ for (Map.Entry<CqlIdentifier, ClusteringOrder> entry :
orderings.entrySet()) {
+ if (first) {
+ builder.append(" ORDER BY ");
+ first = false;
+ } else {
+ builder.append(",");
+ }
+ builder.append(entry.getKey().asCql(true)).append("
").append(entry.getValue().name());
+ }
+ }
+}
diff --git
a/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/OrderingClause.java
b/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/OrderingClause.java
new file mode 100644
index 000000000..0318d115a
--- /dev/null
+++
b/query-builder/src/main/java/com/datastax/oss/driver/api/querybuilder/select/OrderingClause.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.datastax.oss.driver.api.querybuilder.select;
+
+import com.datastax.oss.driver.api.querybuilder.CqlSnippet;
+
+/**
+ * Abstract representation of an ordering clause (i.e. ORDER BY) in a CQL
statement.
+ * Alternate implementations may be provided if servers wind up implementing
customized
+ * orderings.
+ */
+public abstract class OrderingClause implements CqlSnippet {}
diff --git
a/query-builder/src/main/java/com/datastax/oss/driver/internal/querybuilder/select/DefaultSelect.java
b/query-builder/src/main/java/com/datastax/oss/driver/internal/querybuilder/select/DefaultSelect.java
index 5daf252a9..e4b98ef6f 100644
---
a/query-builder/src/main/java/com/datastax/oss/driver/internal/querybuilder/select/DefaultSelect.java
+++
b/query-builder/src/main/java/com/datastax/oss/driver/internal/querybuilder/select/DefaultSelect.java
@@ -23,8 +23,10 @@ import
com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder;
import com.datastax.oss.driver.api.core.data.CqlVector;
import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder;
import com.datastax.oss.driver.api.querybuilder.BindMarker;
-import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.querybuilder.relation.Relation;
+import com.datastax.oss.driver.api.querybuilder.select.AnnOrderingClause;
+import com.datastax.oss.driver.api.querybuilder.select.ColumnsOrderingClause;
+import com.datastax.oss.driver.api.querybuilder.select.OrderingClause;
import com.datastax.oss.driver.api.querybuilder.select.Select;
import com.datastax.oss.driver.api.querybuilder.select.SelectFrom;
import com.datastax.oss.driver.api.querybuilder.select.Selector;
@@ -32,10 +34,10 @@ import
com.datastax.oss.driver.internal.querybuilder.CqlHelper;
import com.datastax.oss.driver.internal.querybuilder.ImmutableCollections;
import com.datastax.oss.driver.shaded.guava.common.base.Preconditions;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
-import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Map;
+import java.util.Optional;
import net.jcip.annotations.Immutable;
@Immutable
@@ -50,8 +52,7 @@ public class DefaultSelect implements SelectFrom, Select {
private final ImmutableList<Selector> selectors;
private final ImmutableList<Relation> relations;
private final ImmutableList<Selector> groupByClauses;
- private final ImmutableMap<CqlIdentifier, ClusteringOrder> orderings;
- private final Ann ann;
+ private final Optional<OrderingClause> orderingClause;
private final Object limit;
private final Object perPartitionLimit;
private final boolean allowsFiltering;
@@ -65,8 +66,7 @@ public class DefaultSelect implements SelectFrom, Select {
ImmutableList.of(),
ImmutableList.of(),
ImmutableList.of(),
- ImmutableMap.of(),
- null,
+ Optional.empty(),
null,
null,
false);
@@ -78,8 +78,6 @@ public class DefaultSelect implements SelectFrom, Select {
* @param selectors if it contains {@link AllSelector#INSTANCE}, that must
be the only element.
* This isn't re-checked because methods that call this constructor
internally already do it,
* make sure you do it yourself.
- * @param ann Approximate nearest neighbor. ANN ordering does not support
secondary ordering or
- * ASC order.
*/
public DefaultSelect(
@Nullable CqlIdentifier keyspace,
@@ -89,21 +87,17 @@ public class DefaultSelect implements SelectFrom, Select {
@NonNull ImmutableList<Selector> selectors,
@NonNull ImmutableList<Relation> relations,
@NonNull ImmutableList<Selector> groupByClauses,
- @NonNull ImmutableMap<CqlIdentifier, ClusteringOrder> orderings,
- @Nullable Ann ann,
+ @NonNull Optional<OrderingClause> orderingClause,
@Nullable Object limit,
@Nullable Object perPartitionLimit,
boolean allowsFiltering) {
this.groupByClauses = groupByClauses;
- this.orderings = orderings;
+ this.orderingClause = orderingClause;
Preconditions.checkArgument(
limit == null
|| (limit instanceof Integer && (Integer) limit > 0)
|| limit instanceof BindMarker,
"limit must be a strictly positive integer or a bind marker");
- Preconditions.checkArgument(
- orderings.isEmpty() || ann == null, "ANN ordering does not support
secondary ordering");
- this.ann = ann;
this.keyspace = keyspace;
this.table = table;
this.isJson = isJson;
@@ -126,8 +120,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
perPartitionLimit,
allowsFiltering);
@@ -144,8 +137,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
perPartitionLimit,
allowsFiltering);
@@ -204,8 +196,7 @@ public class DefaultSelect implements SelectFrom, Select {
newSelectors,
relations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
perPartitionLimit,
allowsFiltering);
@@ -233,8 +224,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
newRelations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
perPartitionLimit,
allowsFiltering);
@@ -262,39 +252,54 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
newGroupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
perPartitionLimit,
allowsFiltering);
}
+ /**
+ * Retrieve the current {@link OrderingClause} as a {@link
ColumnsOrderingClause} if it exists and
+ * is an instance of this class, Otherwise create a new one.
+ *
+ * @return the current OrderingClause if it's a ColumnsOrderingClause or a
new one otherwise
+ */
+ private ColumnsOrderingClause getColumnOrderingClause() {
+ return (ColumnsOrderingClause)
+ orderingClause
+ .map(
+ (oc) -> (oc instanceof ColumnsOrderingClause) ? oc :
ColumnsOrderingClause.create())
+ .orElseGet(() -> ColumnsOrderingClause.create());
+ }
+
@NonNull
@Override
public Select orderBy(@NonNull CqlIdentifier columnId, @NonNull
ClusteringOrder order) {
- return withOrderings(ImmutableCollections.append(orderings, columnId,
order));
+ ColumnsOrderingClause coc = getColumnOrderingClause();
+ return withOrderingClause(coc.add(columnId, order));
}
@NonNull
@Override
public Select orderByAnnOf(@NonNull String columnName, @NonNull CqlVector<?>
ann) {
- return withAnn(new Ann(CqlIdentifier.fromCql(columnName), ann));
+ return
withOrderingClause(AnnOrderingClause.create(CqlIdentifier.fromCql(columnName),
ann));
}
@NonNull
@Override
public Select orderByAnnOf(@NonNull CqlIdentifier columnId, @NonNull
CqlVector<?> ann) {
- return withAnn(new Ann(columnId, ann));
+ return withOrderingClause(AnnOrderingClause.create(columnId, ann));
}
@NonNull
@Override
public Select orderByIds(@NonNull Map<CqlIdentifier, ClusteringOrder>
newOrderings) {
- return withOrderings(ImmutableCollections.concat(orderings, newOrderings));
+ ColumnsOrderingClause coc = getColumnOrderingClause();
+ return withOrderingClause(coc.add(newOrderings));
}
@NonNull
- public Select withOrderings(@NonNull ImmutableMap<CqlIdentifier,
ClusteringOrder> newOrderings) {
+ public Select withOrderingClause(@NonNull OrderingClause newOrderingClause) {
return new DefaultSelect(
keyspace,
table,
@@ -303,25 +308,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
groupByClauses,
- newOrderings,
- ann,
- limit,
- perPartitionLimit,
- allowsFiltering);
- }
-
- @NonNull
- Select withAnn(@NonNull Ann ann) {
- return new DefaultSelect(
- keyspace,
- table,
- isJson,
- isDistinct,
- selectors,
- relations,
- groupByClauses,
- orderings,
- ann,
+ Optional.of(newOrderingClause),
limit,
perPartitionLimit,
allowsFiltering);
@@ -339,8 +326,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
perPartitionLimit,
allowsFiltering);
@@ -357,8 +343,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
bindMarker,
perPartitionLimit,
allowsFiltering);
@@ -377,8 +362,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
perPartitionLimit,
allowsFiltering);
@@ -395,8 +379,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
bindMarker,
allowsFiltering);
@@ -413,8 +396,7 @@ public class DefaultSelect implements SelectFrom, Select {
selectors,
relations,
groupByClauses,
- orderings,
- ann,
+ orderingClause,
limit,
perPartitionLimit,
true);
@@ -441,21 +423,7 @@ public class DefaultSelect implements SelectFrom, Select {
CqlHelper.append(relations, builder, " WHERE ", " AND ", null);
CqlHelper.append(groupByClauses, builder, " GROUP BY ", ",", null);
- if (ann != null) {
- builder.append(" ORDER BY
").append(this.ann.columnId.asCql(true)).append(" ANN OF ");
- QueryBuilder.literal(ann.vector).appendTo(builder);
- } else {
- boolean first = true;
- for (Map.Entry<CqlIdentifier, ClusteringOrder> entry :
orderings.entrySet()) {
- if (first) {
- builder.append(" ORDER BY ");
- first = false;
- } else {
- builder.append(",");
- }
- builder.append(entry.getKey().asCql(true)).append("
").append(entry.getValue().name());
- }
- }
+ orderingClause.ifPresent(c -> c.appendTo(builder));
if (limit != null) {
builder.append(" LIMIT ");
@@ -545,8 +513,8 @@ public class DefaultSelect implements SelectFrom, Select {
}
@NonNull
- public ImmutableMap<CqlIdentifier, ClusteringOrder> getOrderings() {
- return orderings;
+ public Optional<OrderingClause> getOrderingClause() {
+ return orderingClause;
}
@Nullable
@@ -554,11 +522,6 @@ public class DefaultSelect implements SelectFrom, Select {
return limit;
}
- @Nullable
- public Ann getAnn() {
- return ann;
- }
-
@Nullable
public Object getPerPartitionLimit() {
return perPartitionLimit;
@@ -572,14 +535,4 @@ public class DefaultSelect implements SelectFrom, Select {
public String toString() {
return asCql();
}
-
- public static class Ann {
- private final CqlVector<?> vector;
- private final CqlIdentifier columnId;
-
- private Ann(CqlIdentifier columnId, CqlVector<?> vector) {
- this.vector = vector;
- this.columnId = columnId;
- }
- }
}
diff --git
a/query-builder/src/test/java/com/datastax/oss/driver/api/querybuilder/select/SelectOrderingTest.java
b/query-builder/src/test/java/com/datastax/oss/driver/api/querybuilder/select/SelectOrderingTest.java
index a9c618e95..04831bb0f 100644
---
a/query-builder/src/test/java/com/datastax/oss/driver/api/querybuilder/select/SelectOrderingTest.java
+++
b/query-builder/src/test/java/com/datastax/oss/driver/api/querybuilder/select/SelectOrderingTest.java
@@ -86,12 +86,25 @@ public class SelectOrderingTest {
.hasCql("SELECT * FROM foo WHERE k=1 ORDER BY c1 ANN OF [0.1, 0.2,
0.3]");
}
- @Test(expected = IllegalArgumentException.class)
- public void should_fail_when_provided_ann_with_other_orderings() {
- selectFrom("foo")
- .all()
- .where(Relation.column("k").isEqualTo(literal(1)))
- .orderBy("c1", ASC)
- .orderByAnnOf("c2", CqlVector.newInstance(0.1, 0.2, 0.3));
+ @Test
+ public void should_replace_columns_ordering_with_ann() {
+ assertThat(
+ selectFrom("foo")
+ .all()
+ .where(Relation.column("k").isEqualTo(literal(1)))
+ .orderBy("c1", ASC)
+ .orderByAnnOf("c2", CqlVector.newInstance(0.1, 0.2, 0.3)))
+ .hasCql("SELECT * FROM foo WHERE k=1 ORDER BY c2 ANN OF [0.1, 0.2,
0.3]");
+ }
+
+ @Test
+ public void should_replace_ann_ordering_with_columns() {
+ assertThat(
+ selectFrom("foo")
+ .all()
+ .where(Relation.column("k").isEqualTo(literal(1)))
+ .orderByAnnOf("c1", CqlVector.newInstance(0.1, 0.2, 0.3))
+ .orderBy("c2", ASC))
+ .hasCql("SELECT * FROM foo WHERE k=1 ORDER BY c2 ASC");
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]