Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ public void write(JsonWriter out, VectorConfig value) throws IOException {
vectorizer.add(value._kind().jsonValue(), config);
vectorIndex.getAsJsonObject().add("vectorizer", vectorizer);

if (value.quantization() != null) {
if (value.quantization() != null && !config.getAsJsonObject().get("quantization").isJsonNull()) {
vectorIndex.getAsJsonObject()
.get("vectorIndexConfig").getAsJsonObject()
.add(value.quantization()._kind().jsonValue(), config.getAsJsonObject().remove("quantization"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import io.weaviate.client6.v1.api.collections.vectorindex.Dynamic;
import io.weaviate.client6.v1.api.collections.vectorindex.Flat;
import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw;
import io.weaviate.client6.v1.internal.TaggedUnion;
import io.weaviate.client6.v1.internal.json.JsonEnum;

public interface VectorIndex {
public interface VectorIndex extends TaggedUnion<VectorIndex.Kind, Object> {
static final String DEFAULT_VECTOR_NAME = "default";
static final VectorIndex DEFAULT_VECTOR_INDEX = Hnsw.of();

public enum Kind implements JsonEnum<Kind> {
enum Kind implements JsonEnum<Kind> {
HNSW("hnsw"),
FLAT("flat"),
DYNAMIC("dynamic");
Expand All @@ -43,17 +45,37 @@ public static Kind valueOfJson(String jsonValue) {
}
}

VectorIndex.Kind _kind();
/** Is this vector index of type HNSW? */
default Hnsw isHnsw() {
return _as(VectorIndex.Kind.HNSW);
}

/** Get as {@link Hnsw} instance. */
default Hnsw asHnsw() {
return _as(VectorIndex.Kind.HNSW);
}

/** Is this vector index of type FLAT? */
default Flat isFlat() {
return _as(VectorIndex.Kind.FLAT);
}

/** Get as {@link Flat} instance. */
default Flat asFlat() {
return _as(VectorIndex.Kind.FLAT);
}

/** Returns the on-the-wire name of the vector index type. */
default String type() {
return _kind().jsonValue();
/** Is this vector index of type DYNAMIC? */
default Dynamic isDynamic() {
return _as(VectorIndex.Kind.DYNAMIC);
}

/** Get the concrete vector index configuration object. */
Object config();
/** Get as {@link Dynamic} instance. */
default Dynamic asDynamic() {
return _as(VectorIndex.Kind.DYNAMIC);
}

public static enum CustomTypeAdapterFactory implements TypeAdapterFactory {
static enum CustomTypeAdapterFactory implements TypeAdapterFactory {
INSTANCE;

private static final EnumMap<VectorIndex.Kind, TypeAdapter<? extends VectorIndex>> readAdapters = new EnumMap<>(
Expand All @@ -66,6 +88,7 @@ private final void addAdapter(Gson gson, VectorIndex.Kind kind, Class<? extends
private final void init(Gson gson) {
addAdapter(gson, VectorIndex.Kind.HNSW, Hnsw.class);
addAdapter(gson, VectorIndex.Kind.FLAT, Flat.class);
addAdapter(gson, VectorIndex.Kind.DYNAMIC, Dynamic.class);
}

@SuppressWarnings("unchecked")
Expand All @@ -90,7 +113,7 @@ public void write(JsonWriter out, VectorIndex value) throws IOException {
out.value(value._kind().jsonValue());

out.name("vectorIndexConfig");
var config = writeAdapter.toJsonTree((T) value.config());
var config = writeAdapter.toJsonTree((T) value._self());
config.getAsJsonObject().remove("name");
Streams.write(config, out);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.weaviate.client6.v1.api.collections.vectorindex;

import java.io.IOException;
import java.util.function.Function;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.SerializedName;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import io.weaviate.client6.v1.api.collections.VectorIndex;
import io.weaviate.client6.v1.internal.ObjectBuilder;

public record Dynamic(
@SerializedName("hnsw") Hnsw hnsw,
@SerializedName("flat") Flat flat,
@SerializedName("threshold") Long threshold)
implements VectorIndex {

@Override
public VectorIndex.Kind _kind() {
return VectorIndex.Kind.DYNAMIC;
}

@Override
public Object _self() {
return this;
}

public static Dynamic of() {
return of(ObjectBuilder.identity());
}

public static Dynamic of(Function<Builder, ObjectBuilder<Dynamic>> fn) {
return fn.apply(new Builder()).build();
}

public Dynamic(Builder builder) {
this(
builder.hnsw,
builder.flat,
builder.threshold);
}

public static class Builder implements ObjectBuilder<Dynamic> {

private Hnsw hnsw;
private Flat flat;
private Long threshold;

public Builder hnsw(Hnsw hnsw) {
this.hnsw = hnsw;
return this;
}

public Builder flat(Flat flat) {
this.flat = flat;
return this;
}

public Builder threshold(long threshold) {
this.threshold = threshold;
return this;
}

@Override
public Dynamic build() {
return new Dynamic(this);
}
}

public static enum CustomTypeAdapterFactory implements TypeAdapterFactory {
INSTANCE;

@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
var rawType = type.getRawType();
if (!Dynamic.class.isAssignableFrom(rawType)) {
return null;
}

final var hnswAdapter = gson.getDelegateAdapter(VectorIndex.CustomTypeAdapterFactory.INSTANCE,
TypeToken.get(Hnsw.class));
final var flatAdapter = gson.getDelegateAdapter(VectorIndex.CustomTypeAdapterFactory.INSTANCE,
TypeToken.get(Flat.class));

return (TypeAdapter<T>) new TypeAdapter<Dynamic>() {

@Override
public void write(JsonWriter out, Dynamic value) throws IOException {

var dynamic = new JsonObject();

dynamic.addProperty("threshold", value.threshold);
dynamic.add("hnsw", hnswAdapter.toJsonTree(value.hnsw));
dynamic.add("flat", flatAdapter.toJsonTree(value.flat));

Streams.write(dynamic, out);
}

@Override
public Dynamic read(JsonReader in) throws IOException {
var jsonObject = JsonParser.parseReader(in).getAsJsonObject();

var hnsw = hnswAdapter.fromJsonTree(jsonObject.get("hnsw"));
var flat = flatAdapter.fromJsonTree(jsonObject.get("flat"));
var threshold = jsonObject.get("threshold").getAsLong();
return new Dynamic(hnsw, flat, threshold);
}
}.nullSafe();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public VectorIndex.Kind _kind() {
}

@Override
public Object config() {
public Object _self() {
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public VectorIndex.Kind _kind() {
}

@Override
public Object config() {
public Object _self() {
return this;
}

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/io/weaviate/client6/v1/internal/json/JSON.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,17 @@ public final class JSON {
io.weaviate.client6.v1.api.collections.Vectors.CustomTypeAdapterFactory.INSTANCE);
gsonBuilder.registerTypeAdapterFactory(
io.weaviate.client6.v1.api.collections.VectorConfig.CustomTypeAdapterFactory.INSTANCE);

// These 2 adapters need to be registered in this exact order: Dynamic
// (narrower), VectorIndex (broader).
// When searching for an adapter, Gson will pick the first adapter factory that
// can process the class, and it's important that Dynamic.class is processed by
// this factory.
gsonBuilder.registerTypeAdapterFactory(
io.weaviate.client6.v1.api.collections.vectorindex.Dynamic.CustomTypeAdapterFactory.INSTANCE);
gsonBuilder.registerTypeAdapterFactory(
io.weaviate.client6.v1.api.collections.VectorIndex.CustomTypeAdapterFactory.INSTANCE);

gsonBuilder.registerTypeAdapterFactory(
io.weaviate.client6.v1.api.collections.Reranker.CustomTypeAdapterFactory.INSTANCE);
gsonBuilder.registerTypeAdapterFactory(
Expand Down
23 changes: 23 additions & 0 deletions src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import io.weaviate.client6.v1.api.collections.quantizers.PQ;
import io.weaviate.client6.v1.api.collections.rerankers.CohereReranker;
import io.weaviate.client6.v1.api.collections.vectorindex.Distance;
import io.weaviate.client6.v1.api.collections.vectorindex.Dynamic;
import io.weaviate.client6.v1.api.collections.vectorindex.Flat;
import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw;
import io.weaviate.client6.v1.api.collections.vectorindex.MultiVector;
Expand Down Expand Up @@ -166,6 +167,28 @@ public static Object[][] testCases() {
}
""",
},
{
VectorConfig.class,
SelfProvidedVectorizer.of(none -> none
.vectorIndex(Dynamic.of(idx -> idx
.hnsw(Hnsw.of(hnsw -> hnsw
.ef(1)
.efConstruction(2)))
.flat(Flat.of(flat -> flat
.vectorCacheMaxObjects(100)))
.threshold(5)))),
"""
{
"vectorIndexType": "dynamic",
"vectorizer": {"none": {}},
"vectorIndexConfig": {
"flat": {"vectorCacheMaxObjects": 100},
"hnsw": {"ef": 1, "efConstruction": 2},
"threshold": 5
}
}
""",
},
{
VectorConfig.class,
SelfProvidedVectorizer.of(none -> none
Expand Down