diff --git a/src/it/java/io/weaviate/integration/CollectionsITest.java b/src/it/java/io/weaviate/integration/CollectionsITest.java index 03871d6cf..c4bc1ef19 100644 --- a/src/it/java/io/weaviate/integration/CollectionsITest.java +++ b/src/it/java/io/weaviate/integration/CollectionsITest.java @@ -29,6 +29,7 @@ import io.weaviate.client6.v1.api.collections.config.ShardStatus; import io.weaviate.client6.v1.api.collections.generative.DummyGenerative; import io.weaviate.client6.v1.api.collections.query.BaseQueryOptions; +import io.weaviate.client6.v1.api.collections.quantizers.RQ; import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; import io.weaviate.client6.v1.api.collections.vectorizers.SelfProvidedVectorizer; import io.weaviate.containers.Container; @@ -266,6 +267,37 @@ public void test_updateQuantization_uncompressed() throws IOException { .returns(Quantization.Kind.BQ, Quantization::_kind); } + /** + * The quantizer settings have to survive a round trip through the server. + * + *

+ * They used to be sent under snake_case names ({@code rescore_limit}, + * {@code training_limit}, ...) that Weaviate looks up verbatim and therefore + * never read, so every one of them was dropped on write and came back null. + * Asserting on {@code enabled} alone did not catch it. + */ + @Test + public void test_quantizerSettingsRoundTrip() throws IOException { + // Arrange + var nsThings = ns("Things"); + + var things = client.collections.create(nsThings, + c -> c.vectorConfig(VectorConfig.selfProvided( + self -> self.quantization(Quantization.rq(rq -> rq.rescoreLimit(42).bits(8)))))); + + // Act + var config = things.config.get(); + + // Assert + Assertions.assertThat(config).get() + .extracting(CollectionConfig::vectors) + .extracting("default", InstanceOfAssertFactories.type(VectorConfig.class)) + .extracting(VectorConfig::quantization) + .asInstanceOf(InstanceOfAssertFactories.type(RQ.class)) + .returns(42, RQ::rescoreLimit) + .returns(8, RQ::bits); + } + @Test public void test_updateGenerative() throws IOException { // Arrange diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/BQ.java b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/BQ.java index 9d4cdb691..bcc24d539 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/BQ.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/BQ.java @@ -9,7 +9,7 @@ public record BQ( @SerializedName("enabled") boolean enabled, - @SerializedName("rescore_limit") Integer rescoreLimit, + @SerializedName("rescoreLimit") Integer rescoreLimit, @SerializedName("cache") Boolean cache) implements Quantization { @Override diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/PQ.java b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/PQ.java index a4806c12b..348367fab 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/PQ.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/PQ.java @@ -11,10 +11,28 @@ public record PQ( @SerializedName("enabled") boolean enabled, @SerializedName("centroids") Integer centroids, @SerializedName("segments") Integer segments, - @SerializedName("encoder_type") EncoderType encoderType, - @SerializedName("encoder_distribution") EncoderDistribution encoderDistribution, - @SerializedName("training_limit") Integer trainingLimit, - @SerializedName("bit_compression") Boolean bitCompression) implements Quantization { + /** + * Encoder settings, which the server nests one level deeper as + * {@code encoder: {type, distribution}}. + */ + @SerializedName("encoder") Encoder encoder, + @SerializedName("trainingLimit") Integer trainingLimit, + @SerializedName("bitCompression") Boolean bitCompression) implements Quantization { + + /** Type of the encoder, or {@code null} if it was left at the server default. */ + public EncoderType encoderType() { + return encoder != null ? encoder.type() : null; + } + + /** Encoder distribution, or {@code null} if left at the server default. */ + public EncoderDistribution encoderDistribution() { + return encoder != null ? encoder.distribution() : null; + } + + public record Encoder( + @SerializedName("type") EncoderType type, + @SerializedName("distribution") EncoderDistribution distribution) { + } public enum EncoderType { @SerializedName("kmeans") @@ -53,8 +71,9 @@ public PQ(Builder builder) { builder.enabled, builder.centroids, builder.segments, - builder.encoderType, - builder.encoderDistribution, + builder.encoderType == null && builder.encoderDistribution == null + ? null + : new Encoder(builder.encoderType, builder.encoderDistribution), builder.trainingLimit, builder.bitCompression); } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/RQ.java b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/RQ.java index 43dbfb2e0..dc153c316 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/RQ.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/RQ.java @@ -9,7 +9,7 @@ public record RQ( @SerializedName("enabled") boolean enabled, - @SerializedName("rescore_limit") Integer rescoreLimit, + @SerializedName("rescoreLimit") Integer rescoreLimit, @SerializedName("bits") Integer bits, @SerializedName("cache") Boolean cache) implements Quantization { diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/SQ.java b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/SQ.java index ccd9f7070..7be39afa3 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/SQ.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/SQ.java @@ -9,8 +9,8 @@ public record SQ( @SerializedName("enabled") boolean enabled, - @SerializedName("rescore_limit") Integer rescoreLimit, - @SerializedName("training_limit") Integer trainingLimit, + @SerializedName("rescoreLimit") Integer rescoreLimit, + @SerializedName("trainingLimit") Integer trainingLimit, @SerializedName("cache") Boolean cache) implements Quantization { @Override diff --git a/src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java b/src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java index e7c0d6203..481859e1f 100644 --- a/src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java +++ b/src/test/java/io/weaviate/client6/v1/internal/json/JSONTest.java @@ -1206,11 +1206,13 @@ public static Object[][] testCases() { "pq": { "enabled": true, "centroids": 8, - "encoder_distribution": "normal", - "encoder_type": "tile", + "encoder": { + "type": "tile", + "distribution": "normal" + }, "segments": 16, - "training_limit": 1024, - "bit_compression": true + "trainingLimit": 1024, + "bitCompression": true } } } @@ -1230,8 +1232,8 @@ public static Object[][] testCases() { "vectorIndexConfig": { "sq": { "enabled": true, - "rescore_limit": 10, - "training_limit": 1024, + "rescoreLimit": 10, + "trainingLimit": 1024, "cache": true } } @@ -1251,7 +1253,7 @@ public static Object[][] testCases() { "vectorIndexConfig": { "rq": { "enabled": true, - "rescore_limit": 10, + "rescoreLimit": 10, "bits": 8 } } @@ -1271,7 +1273,7 @@ public static Object[][] testCases() { "vectorIndexConfig": { "bq": { "enabled": true, - "rescore_limit": 10, + "rescoreLimit": 10, "cache": true } }