Skip to content

Commit c1de067

Browse files
committed
fix: register custom type adapter factory for Dynamic vector index
1 parent f427b89 commit c1de067

4 files changed

Lines changed: 86 additions & 1 deletion

File tree

src/main/java/io/weaviate/client6/v1/api/collections/VectorConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ public void write(JsonWriter out, VectorConfig value) throws IOException {
285285
vectorizer.add(value._kind().jsonValue(), config);
286286
vectorIndex.getAsJsonObject().add("vectorizer", vectorizer);
287287

288-
if (value.quantization() != null) {
288+
if (value.quantization() != null && !config.getAsJsonObject().get("quantization").isJsonNull()) {
289289
vectorIndex.getAsJsonObject()
290290
.get("vectorIndexConfig").getAsJsonObject()
291291
.add(value.quantization()._kind().jsonValue(), config.getAsJsonObject().remove("quantization"));

src/main/java/io/weaviate/client6/v1/api/collections/vectorindex/Dynamic.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
package io.weaviate.client6.v1.api.collections.vectorindex;
22

3+
import java.io.IOException;
34
import java.util.function.Function;
45

6+
import com.google.gson.Gson;
7+
import com.google.gson.JsonObject;
8+
import com.google.gson.JsonParser;
9+
import com.google.gson.TypeAdapter;
10+
import com.google.gson.TypeAdapterFactory;
511
import com.google.gson.annotations.SerializedName;
12+
import com.google.gson.internal.Streams;
13+
import com.google.gson.reflect.TypeToken;
14+
import com.google.gson.stream.JsonReader;
15+
import com.google.gson.stream.JsonWriter;
616

717
import io.weaviate.client6.v1.api.collections.VectorIndex;
818
import io.weaviate.client6.v1.internal.ObjectBuilder;
@@ -64,4 +74,47 @@ public Dynamic build() {
6474
return new Dynamic(this);
6575
}
6676
}
77+
78+
public static enum CustomTypeAdapterFactory implements TypeAdapterFactory {
79+
INSTANCE;
80+
81+
@SuppressWarnings("unchecked")
82+
@Override
83+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
84+
var rawType = type.getRawType();
85+
if (!Dynamic.class.isAssignableFrom(rawType)) {
86+
return null;
87+
}
88+
89+
final var hnswAdapter = gson.getDelegateAdapter(VectorIndex.CustomTypeAdapterFactory.INSTANCE,
90+
TypeToken.get(Hnsw.class));
91+
final var flatAdapter = gson.getDelegateAdapter(VectorIndex.CustomTypeAdapterFactory.INSTANCE,
92+
TypeToken.get(Flat.class));
93+
94+
return (TypeAdapter<T>) new TypeAdapter<Dynamic>() {
95+
96+
@Override
97+
public void write(JsonWriter out, Dynamic value) throws IOException {
98+
99+
var dynamic = new JsonObject();
100+
101+
dynamic.addProperty("threshold", value.threshold);
102+
dynamic.add("hnsw", hnswAdapter.toJsonTree(value.hnsw));
103+
dynamic.add("flat", flatAdapter.toJsonTree(value.flat));
104+
105+
Streams.write(dynamic, out);
106+
}
107+
108+
@Override
109+
public Dynamic read(JsonReader in) throws IOException {
110+
var jsonObject = JsonParser.parseReader(in).getAsJsonObject();
111+
112+
var hnsw = hnswAdapter.fromJsonTree(jsonObject.get("hnsw"));
113+
var flat = flatAdapter.fromJsonTree(jsonObject.get("flat"));
114+
var threshold = jsonObject.get("threshold").getAsLong();
115+
return new Dynamic(hnsw, flat, threshold);
116+
}
117+
}.nullSafe();
118+
}
119+
}
67120
}

src/main/java/io/weaviate/client6/v1/internal/json/JSON.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,17 @@ public final class JSON {
2828
io.weaviate.client6.v1.api.collections.Vectors.CustomTypeAdapterFactory.INSTANCE);
2929
gsonBuilder.registerTypeAdapterFactory(
3030
io.weaviate.client6.v1.api.collections.VectorConfig.CustomTypeAdapterFactory.INSTANCE);
31+
32+
// These 2 adapters need to be registered in this exact order: Dynamic
33+
// (narrower), VectorIndex (broader).
34+
// When searching for an adapter, Gson will pick the first adapter factory that
35+
// can process the class, and it's important that Dynamic.class is processed by
36+
// this factory.
37+
gsonBuilder.registerTypeAdapterFactory(
38+
io.weaviate.client6.v1.api.collections.vectorindex.Dynamic.CustomTypeAdapterFactory.INSTANCE);
3139
gsonBuilder.registerTypeAdapterFactory(
3240
io.weaviate.client6.v1.api.collections.VectorIndex.CustomTypeAdapterFactory.INSTANCE);
41+
3342
gsonBuilder.registerTypeAdapterFactory(
3443
io.weaviate.client6.v1.api.collections.Reranker.CustomTypeAdapterFactory.INSTANCE);
3544
gsonBuilder.registerTypeAdapterFactory(

src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import io.weaviate.client6.v1.api.collections.quantizers.PQ;
3333
import io.weaviate.client6.v1.api.collections.rerankers.CohereReranker;
3434
import io.weaviate.client6.v1.api.collections.vectorindex.Distance;
35+
import io.weaviate.client6.v1.api.collections.vectorindex.Dynamic;
3536
import io.weaviate.client6.v1.api.collections.vectorindex.Flat;
3637
import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw;
3738
import io.weaviate.client6.v1.api.collections.vectorindex.MultiVector;
@@ -166,6 +167,28 @@ public static Object[][] testCases() {
166167
}
167168
""",
168169
},
170+
{
171+
VectorConfig.class,
172+
SelfProvidedVectorizer.of(none -> none
173+
.vectorIndex(Dynamic.of(idx -> idx
174+
.hnsw(Hnsw.of(hnsw -> hnsw
175+
.ef(1)
176+
.efConstruction(2)))
177+
.flat(Flat.of(flat -> flat
178+
.vectorCacheMaxObjects(100)))
179+
.threshold(5)))),
180+
"""
181+
{
182+
"vectorIndexType": "dynamic",
183+
"vectorizer": {"none": {}},
184+
"vectorIndexConfig": {
185+
"flat": {"vectorCacheMaxObjects": 100},
186+
"hnsw": {"ef": 1, "efConstruction": 2},
187+
"threshold": 5
188+
}
189+
}
190+
""",
191+
},
169192
{
170193
VectorConfig.class,
171194
SelfProvidedVectorizer.of(none -> none

0 commit comments

Comments
 (0)