Skip to content

Commit cfeb55f

Browse files
authored
JAVA-3060: Add vector type, codec + support for parsing CQL type (#1639)
1 parent f91979f commit cfeb55f

File tree

17 files changed

+593
-6
lines changed

17 files changed

+593
-6
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright DataStax, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.datastax.oss.driver.api.core.data;
17+
18+
import com.datastax.oss.driver.shaded.guava.common.base.Joiner;
19+
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
20+
import com.datastax.oss.driver.shaded.guava.common.collect.Iterators;
21+
import java.util.Arrays;
22+
23+
/** An n-dimensional vector defined in CQL */
24+
public class CqlVector<T> {
25+
26+
private final ImmutableList<T> values;
27+
28+
private CqlVector(ImmutableList<T> values) {
29+
this.values = values;
30+
}
31+
32+
public static Builder builder() {
33+
return new Builder();
34+
}
35+
36+
public Iterable<T> getValues() {
37+
return values;
38+
}
39+
40+
@Override
41+
public boolean equals(Object o) {
42+
if (o == this) {
43+
return true;
44+
} else if (o instanceof CqlVector) {
45+
CqlVector that = (CqlVector) o;
46+
return this.values.equals(that.values);
47+
} else {
48+
return false;
49+
}
50+
}
51+
52+
@Override
53+
public int hashCode() {
54+
return Arrays.hashCode(values.toArray());
55+
}
56+
57+
@Override
58+
public String toString() {
59+
60+
String contents = Joiner.on(", ").join(this.values);
61+
return "CqlVector{" + contents + '}';
62+
}
63+
64+
public static class Builder<T> {
65+
66+
private ImmutableList.Builder<T> listBuilder;
67+
68+
private Builder() {
69+
listBuilder = new ImmutableList.Builder<T>();
70+
}
71+
72+
public Builder add(T element) {
73+
listBuilder.add(element);
74+
return this;
75+
}
76+
77+
public Builder add(T... elements) {
78+
listBuilder.addAll(Iterators.forArray(elements));
79+
return this;
80+
}
81+
82+
public Builder addAll(Iterable<T> iter) {
83+
listBuilder.addAll(iter);
84+
return this;
85+
}
86+
87+
public CqlVector<T> build() {
88+
return new CqlVector<T>(listBuilder.build());
89+
}
90+
}
91+
}

core/src/main/java/com/datastax/oss/driver/api/core/data/GettableById.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,24 @@ default CqlDuration getCqlDuration(@NonNull CqlIdentifier id) {
515515
return getCqlDuration(firstIndexOf(id));
516516
}
517517

518+
/**
519+
* Returns the value for the first occurrence of {@code id} as a vector.
520+
*
521+
* <p>By default, this works with CQL type {@code vector}.
522+
*
523+
* <p>If an identifier appears multiple times, this can only be used to access the first value.
524+
* For the other ones, use positional getters.
525+
*
526+
* <p>If you want to avoid the overhead of building a {@code CqlIdentifier}, use the variant of
527+
* this method that takes a string argument.
528+
*
529+
* @throws IllegalArgumentException if the id is invalid.
530+
*/
531+
@Nullable
532+
default CqlVector<?> getCqlVector(@NonNull CqlIdentifier id) {
533+
return getCqlVector(firstIndexOf(id));
534+
}
535+
518536
/**
519537
* Returns the value for the first occurrence of {@code id} as a token.
520538
*

core/src/main/java/com/datastax/oss/driver/api/core/data/GettableByIndex.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,18 @@ default CqlDuration getCqlDuration(int i) {
436436
return get(i, CqlDuration.class);
437437
}
438438

439+
/**
440+
* Returns the {@code i}th value as a vector.
441+
*
442+
* <p>By default, this works with CQL type {@code vector}.
443+
*
444+
* @throws IndexOutOfBoundsException if the index is invalid.
445+
*/
446+
@Nullable
447+
default CqlVector<?> getCqlVector(int i) {
448+
return get(i, CqlVector.class);
449+
}
450+
439451
/**
440452
* Returns the {@code i}th value as a token.
441453
*

core/src/main/java/com/datastax/oss/driver/api/core/data/GettableByName.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,24 @@ default CqlDuration getCqlDuration(@NonNull String name) {
511511
return getCqlDuration(firstIndexOf(name));
512512
}
513513

514+
/**
515+
* Returns the value for the first occurrence of {@code name} as a vector.
516+
*
517+
* <p>By default, this works with CQL type {@code vector}.
518+
*
519+
* <p>If an identifier appears multiple times, this can only be used to access the first value.
520+
* For the other ones, use positional getters.
521+
*
522+
* <p>This method deals with case sensitivity in the way explained in the documentation of {@link
523+
* AccessibleByName}.
524+
*
525+
* @throws IllegalArgumentException if the name is invalid.
526+
*/
527+
@Nullable
528+
default CqlVector<?> getCqlVector(@NonNull String name) {
529+
return getCqlVector(firstIndexOf(name));
530+
}
531+
514532
/**
515533
* Returns the value for the first occurrence of {@code name} as a token.
516534
*

core/src/main/java/com/datastax/oss/driver/api/core/data/SettableById.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,27 @@ default SelfT setCqlDuration(@NonNull CqlIdentifier id, @Nullable CqlDuration v)
559559
return result;
560560
}
561561

562+
/**
563+
* Sets the value for all occurrences of {@code id} to the provided duration.
564+
*
565+
* <p>By default, this works with CQL type {@code vector}.
566+
*
567+
* <p>If you want to avoid the overhead of building a {@code CqlIdentifier}, use the variant of
568+
* this method that takes a string argument.
569+
*
570+
* @throws IllegalArgumentException if the id is invalid.
571+
*/
572+
@NonNull
573+
@CheckReturnValue
574+
default SelfT setCqlVector(@NonNull CqlIdentifier id, @Nullable CqlVector<?> v) {
575+
SelfT result = null;
576+
for (Integer i : allIndicesOf(id)) {
577+
result = (result == null ? this : result).setCqlVector(i, v);
578+
}
579+
assert result != null; // allIndices throws if there are no results
580+
return result;
581+
}
582+
562583
/**
563584
* Sets the value for all occurrences of {@code id} to the provided token.
564585
*

core/src/main/java/com/datastax/oss/driver/api/core/data/SettableByIndex.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,19 @@ default SelfT setCqlDuration(int i, @Nullable CqlDuration v) {
414414
return set(i, v, CqlDuration.class);
415415
}
416416

417+
/**
418+
* Sets the {@code i}th value to the provided duration.
419+
*
420+
* <p>By default, this works with CQL type {@code vector}.
421+
*
422+
* @throws IndexOutOfBoundsException if the index is invalid.
423+
*/
424+
@NonNull
425+
@CheckReturnValue
426+
default SelfT setCqlVector(int i, @Nullable CqlVector<?> v) {
427+
return set(i, v, CqlVector.class);
428+
}
429+
417430
/**
418431
* Sets the {@code i}th value to the provided token.
419432
*

core/src/main/java/com/datastax/oss/driver/api/core/data/SettableByName.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,27 @@ default SelfT setCqlDuration(@NonNull String name, @Nullable CqlDuration v) {
558558
return result;
559559
}
560560

561+
/**
562+
* Sets the value for all occurrences of {@code name} to the provided duration.
563+
*
564+
* <p>By default, this works with CQL type {@code vector}.
565+
*
566+
* <p>This method deals with case sensitivity in the way explained in the documentation of {@link
567+
* AccessibleByName}.
568+
*
569+
* @throws IllegalArgumentException if the name is invalid.
570+
*/
571+
@NonNull
572+
@CheckReturnValue
573+
default SelfT setCqlVector(@NonNull String name, @Nullable CqlVector<?> v) {
574+
SelfT result = null;
575+
for (Integer i : allIndicesOf(name)) {
576+
result = (result == null ? this : result).setCqlVector(i, v);
577+
}
578+
assert result != null; // allIndices throws if there are no results
579+
return result;
580+
}
581+
561582
/**
562583
* Sets the value for all occurrences of {@code name} to the provided token.
563584
*
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright DataStax, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.datastax.oss.driver.api.core.type;
17+
18+
import com.datastax.oss.driver.api.core.detach.AttachmentPoint;
19+
import edu.umd.cs.findbugs.annotations.NonNull;
20+
import java.util.Objects;
21+
22+
public class CqlVectorType implements CustomType {
23+
24+
public static final String CQLVECTOR_CLASS_NAME = "org.apache.cassandra.db.marshal.VectorType";
25+
26+
private final DataType subtype;
27+
private final int dimensions;
28+
29+
public CqlVectorType(DataType subtype, int dimensions) {
30+
31+
this.dimensions = dimensions;
32+
this.subtype = subtype;
33+
}
34+
35+
public int getDimensions() {
36+
return this.dimensions;
37+
}
38+
39+
public DataType getSubtype() {
40+
return this.subtype;
41+
}
42+
43+
@NonNull
44+
@Override
45+
public String getClassName() {
46+
return CQLVECTOR_CLASS_NAME;
47+
}
48+
49+
@NonNull
50+
@Override
51+
public String asCql(boolean includeFrozen, boolean pretty) {
52+
return String.format("'%s(%d)'", getClassName(), getDimensions());
53+
}
54+
55+
@Override
56+
public boolean equals(Object o) {
57+
if (o == this) {
58+
return true;
59+
} else if (o instanceof CqlVectorType) {
60+
CqlVectorType that = (CqlVectorType) o;
61+
return that.subtype.equals(this.subtype) && that.dimensions == this.dimensions;
62+
} else {
63+
return false;
64+
}
65+
}
66+
67+
@Override
68+
public int hashCode() {
69+
return Objects.hash(super.hashCode(), subtype, dimensions);
70+
}
71+
72+
@Override
73+
public String toString() {
74+
return String.format("CqlVector(%s, %d)", getSubtype(), getDimensions());
75+
}
76+
77+
@Override
78+
public boolean isDetached() {
79+
return false;
80+
}
81+
82+
@Override
83+
public void attach(@NonNull AttachmentPoint attachmentPoint) {
84+
// nothing to do
85+
}
86+
}

core/src/main/java/com/datastax/oss/driver/api/core/type/DataTypes.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,21 @@
1515
*/
1616
package com.datastax.oss.driver.api.core.type;
1717

18+
import com.datastax.oss.driver.api.core.detach.AttachmentPoint;
1819
import com.datastax.oss.driver.api.core.detach.Detachable;
20+
import com.datastax.oss.driver.internal.core.metadata.schema.parsing.DataTypeClassNameParser;
1921
import com.datastax.oss.driver.internal.core.type.DefaultCustomType;
2022
import com.datastax.oss.driver.internal.core.type.DefaultListType;
2123
import com.datastax.oss.driver.internal.core.type.DefaultMapType;
2224
import com.datastax.oss.driver.internal.core.type.DefaultSetType;
2325
import com.datastax.oss.driver.internal.core.type.DefaultTupleType;
2426
import com.datastax.oss.driver.internal.core.type.PrimitiveType;
27+
import com.datastax.oss.driver.shaded.guava.common.base.Splitter;
2528
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
2629
import com.datastax.oss.protocol.internal.ProtocolConstants;
2730
import edu.umd.cs.findbugs.annotations.NonNull;
2831
import java.util.Arrays;
32+
import java.util.List;
2933

3034
/** Constants and factory methods to obtain data type instances. */
3135
public class DataTypes {
@@ -51,14 +55,26 @@ public class DataTypes {
5155
public static final DataType TINYINT = new PrimitiveType(ProtocolConstants.DataType.TINYINT);
5256
public static final DataType DURATION = new PrimitiveType(ProtocolConstants.DataType.DURATION);
5357

58+
private static final DataTypeClassNameParser classNameParser = new DataTypeClassNameParser();
59+
private static final Splitter paramSplitter = Splitter.on(',').trimResults();
60+
5461
@NonNull
5562
public static DataType custom(@NonNull String className) {
63+
5664
// In protocol v4, duration is implemented as a custom type
57-
if ("org.apache.cassandra.db.marshal.DurationType".equals(className)) {
58-
return DURATION;
59-
} else {
60-
return new DefaultCustomType(className);
65+
if (className.equals("org.apache.cassandra.db.marshal.DurationType")) return DURATION;
66+
67+
/* Vector support is currently implemented as a custom type but is also parameterized */
68+
if (className.startsWith(CqlVectorType.CQLVECTOR_CLASS_NAME)) {
69+
List<String> params =
70+
paramSplitter.splitToList(
71+
className.substring(
72+
CqlVectorType.CQLVECTOR_CLASS_NAME.length() + 1, className.length() - 1));
73+
DataType subType = classNameParser.parse(params.get(0), AttachmentPoint.NONE);
74+
int dimensions = Integer.parseInt(params.get(1));
75+
return new CqlVectorType(subType, dimensions);
6176
}
77+
return new DefaultCustomType(className);
6278
}
6379

6480
@NonNull
@@ -118,4 +134,8 @@ public static MapType frozenMapOf(@NonNull DataType keyType, @NonNull DataType v
118134
public static TupleType tupleOf(@NonNull DataType... componentTypes) {
119135
return new DefaultTupleType(ImmutableList.copyOf(Arrays.asList(componentTypes)));
120136
}
137+
138+
public static CqlVectorType vectorOf(DataType subtype, int dimensions) {
139+
return new CqlVectorType(subtype, dimensions);
140+
}
121141
}

0 commit comments

Comments
 (0)