diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a0acd1121..303bba5d8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,6 +11,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} - name: Login to Docker Hub if: ${{ !github.event.pull_request.head.repo.fork }} uses: docker/login-action@v2 diff --git a/src/it/java/io/weaviate/containers/Weaviate.java b/src/it/java/io/weaviate/containers/Weaviate.java index e1bb7157f..aaeeb30c6 100644 --- a/src/it/java/io/weaviate/containers/Weaviate.java +++ b/src/it/java/io/weaviate/containers/Weaviate.java @@ -78,7 +78,7 @@ public WeaviateClient getNewClient(Function .grpcHost(host) .httpPort(getMappedPort(8080)) .grpcPort(getMappedPort(50051))); - return WeaviateClient.custom(customFn); + return WeaviateClient.connectToCustom(customFn); } public static Weaviate createDefault() { diff --git a/src/it/java/io/weaviate/integration/AggregationITest.java b/src/it/java/io/weaviate/integration/AggregationITest.java index 19d8f3460..30df82fb5 100644 --- a/src/it/java/io/weaviate/integration/AggregationITest.java +++ b/src/it/java/io/weaviate/integration/AggregationITest.java @@ -14,7 +14,7 @@ import io.weaviate.ConcurrentTest; import io.weaviate.client6.v1.api.WeaviateClient; import io.weaviate.client6.v1.api.collections.Property; -import io.weaviate.client6.v1.api.collections.Vectorizers; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.Vectors; import io.weaviate.client6.v1.api.collections.aggregate.Aggregate; import io.weaviate.client6.v1.api.collections.aggregate.AggregateResponseGroup; @@ -35,7 +35,7 @@ public static void beforeAll() throws IOException { .properties( Property.text("category"), Property.integer("price")) - .vectors(Vectorizers.selfProvided())); + .vectorConfig(VectorConfig.selfProvided())); var things = client.collections.use(COLLECTION); for (var category : List.of("Shoes", "Hat", "Jacket")) { diff --git a/src/it/java/io/weaviate/integration/CollectionsITest.java b/src/it/java/io/weaviate/integration/CollectionsITest.java index 732f63c95..7e21784e6 100644 --- a/src/it/java/io/weaviate/integration/CollectionsITest.java +++ b/src/it/java/io/weaviate/integration/CollectionsITest.java @@ -12,9 +12,9 @@ import io.weaviate.client6.v1.api.collections.CollectionConfig; import io.weaviate.client6.v1.api.collections.InvertedIndex; import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.ReferenceProperty; import io.weaviate.client6.v1.api.collections.Replication; -import io.weaviate.client6.v1.api.collections.Vectorizer; -import io.weaviate.client6.v1.api.collections.Vectorizers; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.config.Shard; import io.weaviate.client6.v1.api.collections.config.ShardStatus; import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; @@ -30,18 +30,18 @@ public void testCreateGetDelete() throws IOException { client.collections.create(collectionName, col -> col .properties(Property.text("username"), Property.integer("age")) - .vectors(Vectorizers.selfProvided())); + .vectorConfig(VectorConfig.selfProvided())); var thingsCollection = client.collections.getConfig(collectionName); Assertions.assertThat(thingsCollection).get() .hasFieldOrPropertyWithValue("collectionName", collectionName) - .extracting(CollectionConfig::vectors, InstanceOfAssertFactories.map(String.class, Vectorizer.class)) + .extracting(CollectionConfig::vectors, InstanceOfAssertFactories.map(String.class, VectorConfig.class)) .as("default vector").extractingByKey("default") .satisfies(defaultVector -> { Assertions.assertThat(defaultVector) .as("has none vectorizer").isInstanceOf(SelfProvidedVectorizer.class); - Assertions.assertThat(defaultVector).extracting(Vectorizer::vectorIndex) + Assertions.assertThat(defaultVector).extracting(VectorConfig::vectorIndex) .isInstanceOf(Hnsw.class); }); @@ -59,7 +59,7 @@ public void testCrossReferences() throws IOException { // Act: Create Things collection with owner -> owners var nsThings = ns("Things"); client.collections.create(nsThings, - col -> col.references(Property.reference("ownedBy", nsOwners))); + col -> col.references(ReferenceProperty.to("ownedBy", nsOwners))); var things = client.collections.use(nsThings); // Assert: Things --ownedBy-> Owners diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index 7db8e6b40..c679f7a7a 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -15,7 +15,8 @@ import io.weaviate.client6.v1.api.WeaviateApiException; import io.weaviate.client6.v1.api.WeaviateClient; import io.weaviate.client6.v1.api.collections.Property; -import io.weaviate.client6.v1.api.collections.Vectorizers; +import io.weaviate.client6.v1.api.collections.ReferenceProperty; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.Vectors; import io.weaviate.client6.v1.api.collections.WeaviateObject; import io.weaviate.client6.v1.api.collections.data.BatchReference; @@ -116,8 +117,8 @@ private static void createTestCollections() throws IOException { Property.text("name"), Property.integer("age")) .references( - Property.reference("hasAwards", awardsGrammy, awardsOscar)) - .vectors(Vectorizers.selfProvided(VECTOR_INDEX))); + ReferenceProperty.to("hasAwards", awardsGrammy, awardsOscar)) + .vectorConfig(VectorConfig.selfProvided(VECTOR_INDEX))); } @Test @@ -128,7 +129,7 @@ public void testReferences_AddReplaceDelete() throws IOException { client.collections.create(nsPersons, collection -> collection .properties(Property.text("name")) - .references(Property.reference("hasFriend", nsPersons))); + .references(ReferenceProperty.to("hasFriend", nsPersons))); var persons = client.collections.use(nsPersons); var john = persons.data.insert(Map.of("name", "john")); @@ -232,8 +233,8 @@ public void testUpdate() throws IOException { client.collections.create(nsBooks, collection -> collection .properties(Property.text("title"), Property.integer("year")) - .references(Property.reference("writtenBy", nsAuthors)) - .vectors(Vectorizers.selfProvided())); + .references(ReferenceProperty.to("writtenBy", nsAuthors)) + .vectorConfig(VectorConfig.selfProvided())); var authors = client.collections.use(nsAuthors); var walter = authors.data.insert(Map.of("name", "walter scott")); @@ -364,7 +365,7 @@ public void testReferenceAddMany() throws IOException { client.collections.create(nsAirports); client.collections.create(nsCities, c -> c - .references(Property.reference("hasAirports", nsAirports))); + .references(ReferenceProperty.to("hasAirports", nsAirports))); var airports = client.collections.use(nsAirports); var cities = client.collections.use(nsCities); diff --git a/src/it/java/io/weaviate/integration/ReferencesITest.java b/src/it/java/io/weaviate/integration/ReferencesITest.java index 404a6adfe..53a36a2c1 100644 --- a/src/it/java/io/weaviate/integration/ReferencesITest.java +++ b/src/it/java/io/weaviate/integration/ReferencesITest.java @@ -48,7 +48,7 @@ public void testReferences() throws IOException { Property.text("name"), Property.integer("age")) .references( - Property.reference("hasAwards", nsGrammy, nsOscar))); + ReferenceProperty.to("hasAwards", nsGrammy, nsOscar))); var artists = client.collections.use(nsArtists); var grammies = client.collections.use(nsGrammy); @@ -129,7 +129,7 @@ public void testNestedReferences() throws IOException { // Act: create Artists collection with hasAwards reference client.collections.create(nsGrammy, col -> col - .references(Property.reference("presentedBy", nsAcademy))); + .references(ReferenceProperty.to("presentedBy", nsAcademy))); client.collections.create(nsArtists, col -> col @@ -137,7 +137,7 @@ public void testNestedReferences() throws IOException { Property.text("name"), Property.integer("age")) .references( - Property.reference("hasAwards", nsGrammy))); + ReferenceProperty.to("hasAwards", nsGrammy))); var artists = client.collections.use(nsArtists); var grammies = client.collections.use(nsGrammy); diff --git a/src/it/java/io/weaviate/integration/SearchITest.java b/src/it/java/io/weaviate/integration/SearchITest.java index f14f98582..418fb3189 100644 --- a/src/it/java/io/weaviate/integration/SearchITest.java +++ b/src/it/java/io/weaviate/integration/SearchITest.java @@ -20,7 +20,8 @@ import io.weaviate.client6.v1.api.WeaviateApiException; import io.weaviate.client6.v1.api.WeaviateClient; import io.weaviate.client6.v1.api.collections.Property; -import io.weaviate.client6.v1.api.collections.Vectorizers; +import io.weaviate.client6.v1.api.collections.ReferenceProperty; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.Vectors; import io.weaviate.client6.v1.api.collections.WeaviateMetadata; import io.weaviate.client6.v1.api.collections.WeaviateObject; @@ -133,7 +134,7 @@ private static Map populateTest(int n) throws IOException { private static void createTestCollection() throws IOException { client.collections.create(COLLECTION, cfg -> cfg .properties(Property.text("category")) - .vectors(Vectorizers.selfProvided(VECTOR_INDEX))); + .vectorConfig(VectorConfig.selfProvided(VECTOR_INDEX))); } @Test @@ -142,7 +143,7 @@ public void testNearText() throws IOException { client.collections.create(nsSongs, col -> col .properties(Property.text("title")) - .vectors(Vectorizers.text2vecContextionary())); + .vectorConfig(VectorConfig.text2vecContextionary())); var songs = client.collections.use(nsSongs); var submarine = songs.data.insert(Map.of("title", "Yellow Submarine")); @@ -164,13 +165,13 @@ public void testNearText() throws IOException { @Test public void testNearText_groupBy() throws IOException { - var vectorizer = Vectorizers.text2vecContextionary(); + var vectorizer = VectorConfig.text2vecContextionary(); var nsArtists = ns("Artists"); client.collections.create(nsArtists, col -> col .properties(Property.text("name")) - .vectors(vectorizer)); + .vectorConfig(vectorizer)); var artists = client.collections.use(nsArtists); var beatles = artists.data.insert(Map.of("name", "Beatles")); @@ -180,8 +181,8 @@ public void testNearText_groupBy() throws IOException { client.collections.create(nsSongs, col -> col .properties(Property.text("title")) - .references(Property.reference("performedBy", nsArtists)) - .vectors(vectorizer)); + .references(ReferenceProperty.to("performedBy", nsArtists)) + .vectorConfig(vectorizer)); var songs = client.collections.use(nsSongs); songs.data.insert(Map.of("title", "Yellow Submarine"), @@ -208,7 +209,7 @@ public void testNearImage() throws IOException { .properties( Property.text("breed"), Property.blob("img")) - .vectors(Vectorizers.img2vecNeural( + .vectorConfig(VectorConfig.img2vecNeural( i2v -> i2v.imageFields("img")))); var cats = client.collections.use(nsCats); @@ -390,7 +391,7 @@ public void testNearObject() throws IOException { client.collections.create(nsAnimals, collection -> collection .properties(Property.text("kind")) - .vectors(Vectorizers.text2vecContextionary())); + .vectorConfig(VectorConfig.text2vecContextionary())); var animals = client.collections.use(nsAnimals); @@ -419,7 +420,7 @@ public void testHybrid() throws IOException { client.collections.create(nsHobbies, collection -> collection .properties(Property.text("name"), Property.text("description")) - .vectors(Vectorizers.text2vecContextionary())); + .vectorConfig(VectorConfig.text2vecContextionary())); var hobbies = client.collections.use(nsHobbies); @@ -452,7 +453,7 @@ public void testBadRequest() throws IOException { client.collections.create(nsThings, collection -> collection .properties(Property.text("name")) - .vectors(Vectorizers.text2vecContextionary())); + .vectorConfig(VectorConfig.text2vecContextionary())); var things = client.collections.use(nsThings); var balloon = things.data.insert(Map.of("name", "balloon")); @@ -469,7 +470,7 @@ public void testBadRequest_async() throws Throwable { async.collections.create(nsThings, collection -> collection .properties(Property.text("name")) - .vectors(Vectorizers.text2vecContextionary())) + .vectorConfig(VectorConfig.text2vecContextionary())) .join(); var things = async.collections.use(nsThings); @@ -490,7 +491,7 @@ public void testMetadataAll() throws IOException { client.collections.create(nsThings, c -> c .properties(Property.text("name")) - .vectors(Vectorizers.text2vecContextionary( + .vectorConfig(VectorConfig.text2vecContextionary( t2v -> t2v.sourceProperties("name")))); var things = client.collections.use(nsThings); diff --git a/src/main/java/io/weaviate/client6/v1/api/Config.java b/src/main/java/io/weaviate/client6/v1/api/Config.java index a14f01290..3edc1ffe5 100644 --- a/src/main/java/io/weaviate/client6/v1/api/Config.java +++ b/src/main/java/io/weaviate/client6/v1/api/Config.java @@ -185,7 +185,7 @@ public static class Local extends Builder { public Local() { scheme("http"); host("localhost"); - httpPort(8080); + port(8080); grpcPort(50051); } @@ -200,7 +200,7 @@ public Local host(String host) { } /** Override default HTTP port. */ - public Local httpPort(int port) { + public Local port(int port) { this.httpPort = port; return this; } diff --git a/src/main/java/io/weaviate/client6/v1/api/InstanceMetadata.java b/src/main/java/io/weaviate/client6/v1/api/InstanceMetadata.java index 9664e8490..c3ce05765 100644 --- a/src/main/java/io/weaviate/client6/v1/api/InstanceMetadata.java +++ b/src/main/java/io/weaviate/client6/v1/api/InstanceMetadata.java @@ -8,5 +8,5 @@ public record InstanceMetadata( @SerializedName("hostname") String hostName, @SerializedName("version") String version, @SerializedName("modules") Map modules, - @SerializedName("grpcMaxMessageSize") Long grpcMaxMessageSize) { + @SerializedName("grpcMaxMessageSize") Integer grpcMaxMessageSize) { } diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateApiException.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateApiException.java index 84fffeaee..e91bc1265 100644 --- a/src/main/java/io/weaviate/client6/v1/api/WeaviateApiException.java +++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateApiException.java @@ -1,9 +1,9 @@ package io.weaviate.client6.v1.api; /** - * Exception class thrown by client API message when the request's reached the - * server, but the operation did not complete successfully either either due to - * a bad request or a server error. + * Exception class thrown by client when the request had reached the + * server, but the operation did not complete successfully either + * due to a bad request or a server error. */ public class WeaviateApiException extends WeaviateException { private final String errorMessage; diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java index 970bbcfd2..0101dc122 100644 --- a/src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java +++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateClient.java @@ -32,8 +32,6 @@ public class WeaviateClient implements AutoCloseable { public final WeaviateAliasClient alias; public WeaviateClient(Config config) { - this.config = config; - RestTransportOptions restOpt; GrpcChannelOptions grpcOpt; if (config.authentication() == null) { @@ -52,11 +50,39 @@ public WeaviateClient(Config config) { grpcOpt = config.grpcTransportOptions(tokenProvider); } - this.restTransport = new DefaultRestTransport(restOpt); - this.grpcTransport = new DefaultGrpcTransport(grpcOpt); + // Initialize REST transport to a temporary variable to dispose of + // the associated resources in case we have to throw an exception. + // Assign to this.restTransport only once we're in the clear to + // avoid publishing the object before it's fully initialized. + var _restTransport = new DefaultRestTransport(restOpt); + boolean isLive = false; + InstanceMetadata meta = null; + try { + isLive = _restTransport.performRequest(null, IsLiveRequest._ENDPOINT); + meta = _restTransport.performRequest(null, InstanceMetadataRequest._ENDPOINT); + } catch (IOException e) { + throw new WeaviateConnectException(e); + } + + if (!isLive) { + var ex = new WeaviateConnectException("Weaviate not available at " + restOpt.baseUrl()); + try { + _restTransport.close(); + } catch (Exception e) { + ex.addSuppressed(e); + } + throw ex; + } + + if (meta.grpcMaxMessageSize() != null) { + grpcOpt = grpcOpt.withMaxMessageSize(meta.grpcMaxMessageSize()); + } + this.restTransport = _restTransport; + this.grpcTransport = new DefaultGrpcTransport(grpcOpt); this.alias = new WeaviateAliasClient(restTransport); this.collections = new WeaviateCollectionsClient(restTransport, grpcTransport); + this.config = config; } /** @@ -77,7 +103,7 @@ public WeaviateClient(Config config) { * Example: * *
{@code
-   * var client = WeaviateClient.local();
+   * var client = WeaviateClient.connectToLocal();
    *
    * // Need to make the next request non-blocking
    * try (final var async = client.async()) {
@@ -92,9 +118,9 @@ public WeaviateClient(Config config) {
    * If you only intend to use {@link WeaviateClientAsync}, prefer creating it
    * directly via one of its static factories:
    * 
    - *
  • {@link WeaviateClientAsync#local} - *
  • {@link WeaviateClientAsync#wcd} - *
  • {@link WeaviateClientAsync#custom} + *
  • {@link WeaviateClientAsync#connectToLocal} + *
  • {@link WeaviateClientAsync#connectToWeaviateCloud} + *
  • {@link WeaviateClientAsync#connectToCustom} *
* * Otherwise the client wastes time initializing resources it will never use. @@ -104,29 +130,29 @@ public WeaviateClientAsync async() { } /** Connect to a local Weaviate instance. */ - public static WeaviateClient local() { - return local(ObjectBuilder.identity()); + public static WeaviateClient connectToLocal() { + return connectToLocal(ObjectBuilder.identity()); } /** Connect to a local Weaviate instance. */ - public static WeaviateClient local(Function> fn) { + public static WeaviateClient connectToLocal(Function> fn) { return new WeaviateClient(fn.apply(new Config.Local()).build()); } /** Connect to a Weaviate Cloud instance. */ - public static WeaviateClient wcd(String httpHost, String apiKey) { - return wcd(httpHost, apiKey, ObjectBuilder.identity()); + public static WeaviateClient connectToWeaviateCloud(String httpHost, String apiKey) { + return connectToWeaviateCloud(httpHost, apiKey, ObjectBuilder.identity()); } /** Connect to a Weaviate Cloud instance. */ - public static WeaviateClient wcd(String httpHost, String apiKey, + public static WeaviateClient connectToWeaviateCloud(String httpHost, String apiKey, Function> fn) { var config = new Config.WeaviateCloud(httpHost, Authentication.apiKey(apiKey)); return new WeaviateClient(fn.apply(config).build()); } /** Connect to a Weaviate instance with custom configuration. */ - public static WeaviateClient custom(Function> fn) { + public static WeaviateClient connectToCustom(Function> fn) { return new WeaviateClient(fn.apply(new Config.Custom()).build()); } diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateClientAsync.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateClientAsync.java index 2af8870e9..7255fbc6e 100644 --- a/src/main/java/io/weaviate/client6/v1/api/WeaviateClientAsync.java +++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateClientAsync.java @@ -45,7 +45,7 @@ public WeaviateClientAsync(Config config) { try (final var noAuthRest = new DefaultRestTransport(config.restTransportOptions())) { tokenProvider = config.authentication().getTokenProvider(noAuthRest); } catch (Exception e) { - // Generally IOExceptions are caught in TokenProvider internals. + // Generally exceptions are caught in TokenProvider internals. // This one may be thrown when noAuthRest transport is auto-closed. throw new WeaviateOAuthException(e); } @@ -53,9 +53,36 @@ public WeaviateClientAsync(Config config) { grpcOpt = config.grpcTransportOptions(tokenProvider); } - this.restTransport = new DefaultRestTransport(restOpt); - this.grpcTransport = new DefaultGrpcTransport(grpcOpt); + // Initialize REST transport to a temporary variable to dispose of + // the associated resources in case we have to throw an exception. + // Assign to this.restTransport only once we're in the clear to + // avoid publishing the object before it's fully initialized. + var _restTransport = new DefaultRestTransport(restOpt); + boolean isLive = false; + InstanceMetadata meta = null; + try { + isLive = _restTransport.performRequest(null, IsLiveRequest._ENDPOINT); + meta = _restTransport.performRequest(null, InstanceMetadataRequest._ENDPOINT); + } catch (IOException e) { + throw new WeaviateConnectException(e); + } + if (!isLive) { + var ex = new WeaviateConnectException("Weaviate not available at " + restOpt.baseUrl()); + try { + _restTransport.close(); + } catch (Exception e) { + ex.addSuppressed(e); + } + throw ex; + } + + if (meta.grpcMaxMessageSize() != null) { + grpcOpt = grpcOpt.withMaxMessageSize(meta.grpcMaxMessageSize()); + } + + this.restTransport = _restTransport; + this.grpcTransport = new DefaultGrpcTransport(grpcOpt); this.alias = new WeaviateAliasClientAsync(restTransport); this.collections = new WeaviateCollectionsClientAsync(restTransport, grpcTransport); } @@ -67,8 +94,8 @@ public WeaviateClientAsync(Config config) { * This call is blocking if {@link Authentication} configured, * as the client will need to do the initial token exchange. */ - public static WeaviateClientAsync local() { - return local(ObjectBuilder.identity()); + public static WeaviateClientAsync connectToLocal() { + return connectToLocal(ObjectBuilder.identity()); } /** @@ -78,7 +105,7 @@ public static WeaviateClientAsync local() { * This call is blocking if {@link Authentication} configured, * as the client will need to do the initial token exchange. */ - public static WeaviateClientAsync local(Function> fn) { + public static WeaviateClientAsync connectToLocal(Function> fn) { return new WeaviateClientAsync(fn.apply(new Config.Local()).build()); } @@ -89,8 +116,8 @@ public static WeaviateClientAsync local(Function> fn) { var config = new Config.WeaviateCloud(httpHost, Authentication.apiKey(apiKey)); return new WeaviateClientAsync(fn.apply(config).build()); @@ -113,7 +140,7 @@ public static WeaviateClientAsync wcd(String httpHost, String apiKey, * This call is blocking if {@link Authentication} configured, * as the client will need to do the initial token exchange. */ - public static WeaviateClientAsync custom(Function> fn) { + public static WeaviateClientAsync connectToCustom(Function> fn) { return new WeaviateClientAsync(Config.of(fn)); } diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateConnectException.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateConnectException.java new file mode 100644 index 000000000..feda9c507 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateConnectException.java @@ -0,0 +1,16 @@ +package io.weaviate.client6.v1.api; + +/** Exception thrown if the Weaviate instance appears to be offline. */ +public class WeaviateConnectException extends WeaviateException { + public WeaviateConnectException(String message) { + super(message); + } + + public WeaviateConnectException(String message, Throwable cause) { + super(message, cause); + } + + public WeaviateConnectException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateOAuthException.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateOAuthException.java index 4c2f7931d..61d03d01d 100644 --- a/src/main/java/io/weaviate/client6/v1/api/WeaviateOAuthException.java +++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateOAuthException.java @@ -1,9 +1,8 @@ package io.weaviate.client6.v1.api; /** - * Exception class thrown by client API message when the request's reached the - * server, but the operation did not complete successfully either either due to - * a bad request or a server error. + * Exception throws by the authentication layer if it encountered another + * exception at any point of obtaining the new token or rotating one. */ public class WeaviateOAuthException extends WeaviateException { public WeaviateOAuthException(String message) { diff --git a/src/main/java/io/weaviate/client6/v1/api/WeaviateTransportException.java b/src/main/java/io/weaviate/client6/v1/api/WeaviateTransportException.java new file mode 100644 index 000000000..11ec7ece9 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/WeaviateTransportException.java @@ -0,0 +1,16 @@ +package io.weaviate.client6.v1.api; + +/** Exception thrown by the internal transport layer. Usually not retryable. */ +public class WeaviateTransportException extends WeaviateException { + public WeaviateTransportException(String message) { + super(message); + } + + public WeaviateTransportException(String message, Throwable cause) { + super(message, cause); + } + + public WeaviateTransportException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/CollectionConfig.java b/src/main/java/io/weaviate/client6/v1/api/collections/CollectionConfig.java index 70448ce6a..581c357ee 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/CollectionConfig.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/CollectionConfig.java @@ -27,7 +27,7 @@ public record CollectionConfig( @SerializedName("description") String description, @SerializedName("properties") List properties, List references, - @SerializedName("vectorConfig") Map vectors, + @SerializedName("vectorConfig") Map vectors, @SerializedName("multiTenancyConfig") MultiTenancy multiTenancy, @SerializedName("shardingConfig") Sharding sharding, @SerializedName("replicationConfig") Replication replication, @@ -52,7 +52,7 @@ public Builder edit() { .description(description) .properties(properties) .references(references) - .vectors(vectors) + .vectorConfig(vectors) .multiTenancy(multiTenancy) .sharding(sharding) .replication(replication) @@ -88,7 +88,7 @@ public static class Builder implements ObjectBuilder { private String description; private Map properties = new HashMap<>(); private Map references = new HashMap<>(); - private Map vectors = new HashMap<>(); + private Map vectors = new HashMap<>(); private MultiTenancy multiTenancy; private Sharding sharding; private Replication replication; @@ -131,31 +131,17 @@ private List referenceList() { return this.references.values().stream().toList(); } - public final Builder vectors(Map vectors) { + public final Builder vectorConfig(Map vectors) { this.vectors.putAll(vectors); return this; } @SafeVarargs - public final Builder vectors(Map.Entry... vectors) { + public final Builder vectorConfig(Map.Entry... vectors) { this.vectors.putAll(Map.ofEntries(vectors)); return this; } - public static class VectorsBuilder implements ObjectBuilder> { - private Map vectors = new HashMap<>(); - - public VectorsBuilder vector(String name, VectorIndex vector) { - vectors.put(name, vector); - return this; - } - - @Override - public Map build() { - return this.vectors; - } - } - public Builder sharding(Sharding sharding) { this.sharding = sharding; return this; diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/MultiTenancy.java b/src/main/java/io/weaviate/client6/v1/api/collections/MultiTenancy.java index c02d42539..9524a1f05 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/MultiTenancy.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/MultiTenancy.java @@ -7,7 +7,7 @@ import io.weaviate.client6.v1.internal.ObjectBuilder; public record MultiTenancy( - @SerializedName("enabled") Boolean enabled, + @SerializedName("enabled") boolean enabled, @SerializedName("autoTenantCreation") Boolean createAutomatically, @SerializedName("autoTenantActivation") Boolean activateAutomatically) { @@ -23,7 +23,7 @@ public MultiTenancy(Builder builder) { } public static class Builder implements ObjectBuilder { - private Boolean enabled = true; + private boolean enabled = true; private Boolean createAutomatically; private Boolean activateAutomatically; diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/Property.java b/src/main/java/io/weaviate/client6/v1/api/collections/Property.java index fb5976500..66121675c 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/Property.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/Property.java @@ -1,6 +1,5 @@ package io.weaviate.client6.v1.api.collections; -import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -272,14 +271,6 @@ private static Property newProperty(String name, String dataType, Function collections) { - return new ReferenceProperty(name, collections); - } - /** * Create a new "edit" builder from the property configuration. Consult the { + UNCOMPRESSED("skipDefaultQuantization"), + RQ("rq"), + BQ("bq"), + PQ("pq"), + SQ("sq"); + + private static final Map jsonValueMap = JsonEnum.collectNames(Kind.values()); + private final String jsonValue; + + private Kind(String jsonValue) { + this.jsonValue = jsonValue; + } + + @Override + public String jsonValue() { + return this.jsonValue; + } + + public static Kind valueOfJson(String jsonValue) { + return JsonEnum.valueOfJson(jsonValue, jsonValueMap, Kind.class); + } + } + + Kind _kind(); + + Object _self(); + + public static Quantization uncompressed() { + return Uncompressed.of(); + } + + public static Quantization bq() { + return BQ.of(); + } + + public static Quantization bq(Function> fn) { + return BQ.of(fn); + } + + public static Quantization pq() { + return PQ.of(); + } + + public static Quantization pq(Function> fn) { + return PQ.of(fn); + } + + public static Quantization sq() { + return SQ.of(); + } + + public static Quantization sq(Function> fn) { + return SQ.of(fn); + } + + public static Quantization rq() { + return RQ.of(); + } + + public static Quantization rq(Function> fn) { + return RQ.of(fn); + } + + public static enum CustomTypeAdapterFactory implements TypeAdapterFactory { + INSTANCE; + + private static final EnumMap> delegateAdapters = new EnumMap<>( + Quantization.Kind.class); + + private final void addAdapter(Gson gson, Quantization.Kind kind, Class cls) { + delegateAdapters.put(kind, + (TypeAdapter) gson.getDelegateAdapter(this, TypeToken.get(cls))); + } + + private final void init(Gson gson) { + addAdapter(gson, Quantization.Kind.UNCOMPRESSED, Uncompressed.class); + addAdapter(gson, Quantization.Kind.BQ, BQ.class); + addAdapter(gson, Quantization.Kind.RQ, RQ.class); + addAdapter(gson, Quantization.Kind.SQ, SQ.class); + addAdapter(gson, Quantization.Kind.PQ, PQ.class); + } + + @SuppressWarnings("unchecked") + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + final var rawType = type.getRawType(); + if (!Quantization.class.isAssignableFrom(rawType)) { + return null; + } + + if (delegateAdapters.isEmpty()) { + init(gson); + } + + return (TypeAdapter) new TypeAdapter() { + + @Override + public void write(JsonWriter out, Quantization value) throws IOException { + if (value._kind() == Quantization.Kind.UNCOMPRESSED) { + // out.name(value._kind().jsonValue()); + out.value(true); + return; + } + TypeAdapter adapter = (TypeAdapter) delegateAdapters.get(value._kind()); + adapter.write(out, (T) value._self()); + } + + @Override + public Quantization read(JsonReader in) throws IOException { + var quantizerObject = JsonParser.parseReader(in).getAsJsonObject(); + var quantizationName = quantizerObject.keySet().iterator().next(); + Quantization.Kind kind; + try { + kind = Quantization.Kind.valueOfJson(quantizationName); + } catch (IllegalArgumentException e) { + return null; + } + + if (kind == Quantization.Kind.UNCOMPRESSED) { + return new Uncompressed(); + } + + var adapter = delegateAdapters.get(kind); + var concreteQuantizer = quantizerObject.get(quantizationName).getAsJsonObject(); + return adapter.fromJsonTree(concreteQuantizer); + } + }.nullSafe(); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/ReferenceProperty.java b/src/main/java/io/weaviate/client6/v1/api/collections/ReferenceProperty.java index 6fa0ade98..38716f782 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/ReferenceProperty.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/ReferenceProperty.java @@ -1,5 +1,6 @@ package io.weaviate.client6.v1.api.collections; +import java.util.Arrays; import java.util.List; import com.google.gson.annotations.SerializedName; @@ -8,6 +9,41 @@ public record ReferenceProperty( @SerializedName("name") String propertyName, @SerializedName("dataType") List dataTypes) { + /** + * Create a cross-reference to another collection. + * + *
{@code
+   * // Single-target reference
+   * ReferenceProperty.to("livesIn", "Cities");
+   *
+   * // Multi-reference
+   * ReferenceProperty.to("hasSeen", "Movies", "Plays", "SoapOperas");
+   * }
+ * + * @param name Name of the property. + * @param collections One or more collections which can be referenced. + * @return ReferenceProperty + */ + public static ReferenceProperty to(String name, String... collections) { + return new ReferenceProperty(name, Arrays.asList(collections)); + } + + /** + * Create a multi-target reference property. + * + *
{@code
+   * List thingsToSee = List.of("Movies", "Plays", "SoapOperas");
+   * ReferenceProperty.to("hasSeen", thingsToSee);
+   * }
+ * + * @param name Name of the property. + * @param collections One or more collections which can be referenced. + * @return ReferenceProperty + */ + public static ReferenceProperty to(String name, List collections) { + return new ReferenceProperty(name, collections); + } + public Property toProperty() { return new Property.Builder(propertyName, dataTypes).build(); } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/VectorConfig.java b/src/main/java/io/weaviate/client6/v1/api/collections/VectorConfig.java new file mode 100644 index 000000000..1e8cd96d5 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/VectorConfig.java @@ -0,0 +1,350 @@ +package io.weaviate.client6.v1.api.collections; + +import java.io.IOException; +import java.util.EnumMap; +import java.util.Map; +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.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.vectorizers.Img2VecNeuralVectorizer; +import io.weaviate.client6.v1.api.collections.vectorizers.Multi2VecClipVectorizer; +import io.weaviate.client6.v1.api.collections.vectorizers.SelfProvidedVectorizer; +import io.weaviate.client6.v1.api.collections.vectorizers.Text2VecContextionaryVectorizer; +import io.weaviate.client6.v1.api.collections.vectorizers.Text2VecWeaviateVectorizer; +import io.weaviate.client6.v1.internal.ObjectBuilder; +import io.weaviate.client6.v1.internal.json.JsonEnum; + +public interface VectorConfig { + public enum Kind implements JsonEnum { + NONE("none"), + IMG2VEC_NEURAL("img2vec-neural"), + TEXT2VEC_CONTEXTIONARY("text2vec-contextionary"), + TEXT2VEC_WEAVIATE("text2vec-weaviate"), + MULTI2VEC_CLIP("multi2vec-clip"); + + private static final Map jsonValueMap = JsonEnum.collectNames(Kind.values()); + private final String jsonValue; + + private Kind(String jsonValue) { + this.jsonValue = jsonValue; + } + + @Override + public String jsonValue() { + return this.jsonValue; + } + + public static Kind valueOfJson(String jsonValue) { + return JsonEnum.valueOfJson(jsonValue, jsonValueMap, Kind.class); + } + } + + Kind _kind(); + + Object _self(); + + VectorIndex vectorIndex(); + + Quantization quantization(); + + /** Create a bring-your-own-vector vector index. */ + public static Map.Entry selfProvided() { + return selfProvided(VectorIndex.DEFAULT_VECTOR_NAME); + } + + /** + * Create a bring-your-own-vector vector index. + * + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry selfProvided( + Function> fn) { + return selfProvided(VectorIndex.DEFAULT_VECTOR_NAME, fn); + } + + /** + * Create a named bring-your-own-vector vector index. + * + * @param vectorName Vector name. + */ + public static Map.Entry selfProvided(String vectorName) { + return Map.entry(vectorName, SelfProvidedVectorizer.of()); + } + + /** + * Create a named bring-your-own-vector vector index. + * + * @param vectorName Vector name. + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry selfProvided(String vectorName, + Function> fn) { + return Map.entry(vectorName, SelfProvidedVectorizer.of(fn)); + } + + /** Create a vector index with an {@code img2vec-neural} vectorizer. */ + public static Map.Entry img2vecNeural() { + return img2vecNeural(VectorIndex.DEFAULT_VECTOR_NAME); + } + + /** + * Create a vector index with an {@code img2vec-neural} vectorizer. + * + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry img2vecNeural( + Function> fn) { + return img2vecNeural(VectorIndex.DEFAULT_VECTOR_NAME, fn); + } + + /** + * Create a named vector index with an {@code img2vec-neural} vectorizer. + * + * @param vectorName Vector name. + */ + public static Map.Entry img2vecNeural(String vectorName) { + return Map.entry(vectorName, Img2VecNeuralVectorizer.of()); + } + + /** + * Create a vector index with an {@code img2vec-neural} vectorizer. + * + * @param vectorName Vector name. + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry img2vecNeural(String vectorName, + Function> fn) { + return Map.entry(vectorName, Img2VecNeuralVectorizer.of(fn)); + } + + /** Create a vector index with an {@code multi2vec-clip} vectorizer. */ + public static Map.Entry multi2vecClip() { + return multi2vecClip(VectorIndex.DEFAULT_VECTOR_NAME); + } + + /** + * Create a vector index with an {@code multi2vec-clip} vectorizer. + * + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry multi2vecClip( + Function> fn) { + return multi2vecClip(VectorIndex.DEFAULT_VECTOR_NAME, fn); + } + + /** + * Create a named vector index with an {@code multi2vec-clip} vectorizer. + * + * @param vectorName Vector name. + */ + public static Map.Entry multi2vecClip(String vectorName) { + return Map.entry(vectorName, Multi2VecClipVectorizer.of()); + } + + /** + * Create a named vector index with an {@code multi2vec-clip} vectorizer. + * + * @param vectorName Vector name. + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry multi2vecClip(String vectorName, + Function> fn) { + return Map.entry(vectorName, Multi2VecClipVectorizer.of(fn)); + } + + /** Create a vector index with an {@code text2vec-contextionary} vectorizer. */ + public static Map.Entry text2vecContextionary() { + return text2vecContextionary(VectorIndex.DEFAULT_VECTOR_NAME); + } + + /** + * Create a vector index with an {@code text2vec-contextionary} vectorizer. + * + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry text2vecContextionary( + Function> fn) { + return text2vecContextionary(VectorIndex.DEFAULT_VECTOR_NAME, fn); + } + + /** + * Create a named vector index with an {@code text2vec-contextionary} + * vectorizer. + * + * @param vectorName Vector name. + */ + public static Map.Entry text2vecContextionary(String vectorName) { + return Map.entry(vectorName, Text2VecContextionaryVectorizer.of()); + } + + /** + * Create a named vector index with an {@code text2vec-contextionary} + * vectorizer. + * + * @param vectorName Vector name. + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry text2vecContextionary(String vectorName, + Function> fn) { + return Map.entry(vectorName, Text2VecContextionaryVectorizer.of(fn)); + } + + /** Create a vector index with an {@code text2vec-weaviate} vectorizer. */ + public static Map.Entry text2VecWeaviate() { + return text2VecWeaviate(VectorIndex.DEFAULT_VECTOR_NAME); + } + + /** + * Create a vector index with an {@code text2vec-weaviate} vectorizer. + * + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry text2VecWeaviate( + Function> fn) { + return text2VecWeaviate(VectorIndex.DEFAULT_VECTOR_NAME, fn); + } + + /** + * Create a named vector index with an {@code text2vec-weaviate} vectorizer. + * + * @param vectorName Vector name. + */ + public static Map.Entry text2VecWeaviate(String vectorName) { + return Map.entry(vectorName, Text2VecWeaviateVectorizer.of()); + } + + /** + * Create a named vector index with an {@code text2vec-weaviate} vectorizer. + * + * @param vectorName Vector name. + * @param fn Lambda expression for optional parameters. + */ + public static Map.Entry text2VecWeaviate(String vectorName, + Function> fn) { + return Map.entry(vectorName, Text2VecWeaviateVectorizer.of(fn)); + } + + public static enum CustomTypeAdapterFactory implements TypeAdapterFactory { + INSTANCE; + + private static final EnumMap> delegateAdapters = new EnumMap<>( + VectorConfig.Kind.class); + + private final void addAdapter(Gson gson, VectorConfig.Kind kind, Class cls) { + delegateAdapters.put(kind, + (TypeAdapter) gson.getDelegateAdapter(this, TypeToken.get(cls))); + } + + private final void init(Gson gson) { + addAdapter(gson, VectorConfig.Kind.NONE, SelfProvidedVectorizer.class); + addAdapter(gson, VectorConfig.Kind.IMG2VEC_NEURAL, Img2VecNeuralVectorizer.class); + addAdapter(gson, VectorConfig.Kind.MULTI2VEC_CLIP, Multi2VecClipVectorizer.class); + addAdapter(gson, VectorConfig.Kind.TEXT2VEC_WEAVIATE, Text2VecWeaviateVectorizer.class); + addAdapter(gson, VectorConfig.Kind.TEXT2VEC_CONTEXTIONARY, Text2VecContextionaryVectorizer.class); + } + + @SuppressWarnings("unchecked") + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + final var rawType = type.getRawType(); + if (!VectorConfig.class.isAssignableFrom(rawType)) { + return null; + } + + if (delegateAdapters.isEmpty()) { + init(gson); + } + + return (TypeAdapter) new TypeAdapter() { + + @Override + public void write(JsonWriter out, VectorConfig value) throws IOException { + TypeAdapter adapter = (TypeAdapter) delegateAdapters.get(value._kind()); + + // Serialize vectorizer config as { "vectorizer-kind": { ... } } + // and remove "vectorIndex" and quantization objects which every vectorizer has. + var vectorizer = new JsonObject(); + var config = adapter.toJsonTree((T) value._self()); + + // This will create { "vectorIndexType": "", "vectorIndexConfig": { ... } } + // to which we just need to add "vectorizer": { ... } key + // and "bq"/"pg"/"sq"/"rq": { ... } (quantizer) key. + var vectorIndex = config.getAsJsonObject().remove("vectorIndex"); + + vectorizer.add(value._kind().jsonValue(), config); + vectorIndex.getAsJsonObject().add("vectorizer", vectorizer); + + if (value.quantization() != null) { + vectorIndex.getAsJsonObject() + .get("vectorIndexConfig").getAsJsonObject() + .add(value.quantization()._kind().jsonValue(), config.getAsJsonObject().remove("quantization")); + } + + Streams.write(vectorIndex, out); + } + + @Override + public VectorConfig read(JsonReader in) throws IOException { + var jsonObject = JsonParser.parseReader(in).getAsJsonObject(); + var vectorIndexConfig = jsonObject.get("vectorIndexConfig").getAsJsonObject(); + + String quantizationKind = null; + if (vectorIndexConfig.has(Quantization.Kind.BQ.jsonValue())) { + quantizationKind = Quantization.Kind.BQ.jsonValue(); + } else if (vectorIndexConfig.has(Quantization.Kind.PQ.jsonValue())) { + quantizationKind = Quantization.Kind.PQ.jsonValue(); + } else if (vectorIndexConfig.has(Quantization.Kind.SQ.jsonValue())) { + quantizationKind = Quantization.Kind.SQ.jsonValue(); + } else if (vectorIndexConfig.has(Quantization.Kind.RQ.jsonValue())) { + quantizationKind = Quantization.Kind.RQ.jsonValue(); + } else { + quantizationKind = Quantization.Kind.UNCOMPRESSED.jsonValue(); + } + + // VectorIndex.CustomTypeAdapterFactory expects keys + // ["vectorIndexType", "vectorIndexConfig"]. + var vectorIndex = new JsonObject(); + vectorIndex.add("vectorIndexType", jsonObject.get("vectorIndexType")); + vectorIndex.add("vectorIndexConfig", vectorIndexConfig); + + var vectorizerObject = jsonObject.get("vectorizer").getAsJsonObject(); + var vectorizerName = vectorizerObject.keySet().iterator().next(); + + VectorConfig.Kind kind; + try { + kind = VectorConfig.Kind.valueOfJson(vectorizerName); + } catch (IllegalArgumentException e) { + return null; + } + + var adapter = delegateAdapters.get(kind); + var concreteVectorizer = vectorizerObject.get(vectorizerName).getAsJsonObject(); + + // Each individual vectorizer has a `VectorIndex vectorIndex` field. + concreteVectorizer.add("vectorIndex", vectorIndex); + + // Each individual vectorizer has a `Quantization quantization` field. + // We need to specify the kind in order for + // Quantization.CustomTypeAdapterFactory to be able to find the right adapter. + if (vectorIndexConfig.has(quantizationKind)) { + JsonObject quantization = new JsonObject(); + quantization.add(quantizationKind, vectorIndexConfig.get(quantizationKind)); + concreteVectorizer.add("quantization", quantization); + } else { + concreteVectorizer.add("quantization", null); + } + return adapter.fromJsonTree(concreteVectorizer); + } + }.nullSafe(); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/Vectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/Vectorizer.java deleted file mode 100644 index fed6d21d3..000000000 --- a/src/main/java/io/weaviate/client6/v1/api/collections/Vectorizer.java +++ /dev/null @@ -1,137 +0,0 @@ -package io.weaviate.client6.v1.api.collections; - -import java.io.IOException; -import java.util.EnumMap; -import java.util.Map; - -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.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.vectorizers.Img2VecNeuralVectorizer; -import io.weaviate.client6.v1.api.collections.vectorizers.Multi2VecClipVectorizer; -import io.weaviate.client6.v1.api.collections.vectorizers.SelfProvidedVectorizer; -import io.weaviate.client6.v1.api.collections.vectorizers.Text2VecContextionaryVectorizer; -import io.weaviate.client6.v1.api.collections.vectorizers.Text2VecWeaviateVectorizer; -import io.weaviate.client6.v1.internal.json.JsonEnum; - -public interface Vectorizer { - public enum Kind implements JsonEnum { - NONE("none"), - IMG2VEC_NEURAL("img2vec-neural"), - TEXT2VEC_CONTEXTIONARY("text2vec-contextionary"), - TEXT2VEC_WEAVIATE("text2vec-weaviate"), - MULTI2VEC_CLIP("multi2vec-clip"); - - private static final Map jsonValueMap = JsonEnum.collectNames(Kind.values()); - private final String jsonValue; - - private Kind(String jsonValue) { - this.jsonValue = jsonValue; - } - - @Override - public String jsonValue() { - return this.jsonValue; - } - - public static Kind valueOfJson(String jsonValue) { - return JsonEnum.valueOfJson(jsonValue, jsonValueMap, Kind.class); - } - } - - Kind _kind(); - - Object _self(); - - VectorIndex vectorIndex(); - - public static enum CustomTypeAdapterFactory implements TypeAdapterFactory { - INSTANCE; - - private static final EnumMap> delegateAdapters = new EnumMap<>( - Vectorizer.Kind.class); - - private final void addAdapter(Gson gson, Vectorizer.Kind kind, Class cls) { - delegateAdapters.put(kind, (TypeAdapter) gson.getDelegateAdapter(this, TypeToken.get(cls))); - } - - private final void init(Gson gson) { - addAdapter(gson, Vectorizer.Kind.NONE, SelfProvidedVectorizer.class); - addAdapter(gson, Vectorizer.Kind.IMG2VEC_NEURAL, Img2VecNeuralVectorizer.class); - addAdapter(gson, Vectorizer.Kind.MULTI2VEC_CLIP, Multi2VecClipVectorizer.class); - addAdapter(gson, Vectorizer.Kind.TEXT2VEC_WEAVIATE, Text2VecWeaviateVectorizer.class); - addAdapter(gson, Vectorizer.Kind.TEXT2VEC_CONTEXTIONARY, Text2VecContextionaryVectorizer.class); - } - - @SuppressWarnings("unchecked") - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - final var rawType = type.getRawType(); - if (!Vectorizer.class.isAssignableFrom(rawType)) { - return null; - } - - if (delegateAdapters.isEmpty()) { - init(gson); - } - - return (TypeAdapter) new TypeAdapter() { - - @Override - public void write(JsonWriter out, Vectorizer value) throws IOException { - TypeAdapter adapter = (TypeAdapter) delegateAdapters.get(value._kind()); - - // Serialize vectorizer config as { "vectorizer-kind": { ... } } - // and remove "vectorIndex" object which every vectorizer has. - var vectorizer = new JsonObject(); - var config = adapter.toJsonTree((T) value._self()); - - // This will create { "vectorIndexType": "", "vectorIndexConfig": { ... } } - // to which we just need to add "vectorizer": { ... } key. - var vectorIndex = config.getAsJsonObject().remove("vectorIndex"); - - vectorizer.add(value._kind().jsonValue(), config); - vectorIndex.getAsJsonObject().add("vectorizer", vectorizer); - - Streams.write(vectorIndex, out); - } - - @Override - public Vectorizer read(JsonReader in) throws IOException { - var jsonObject = JsonParser.parseReader(in).getAsJsonObject(); - - // VectorIndex.CustomTypeAdapterFactory expects keys - // ["vectorIndexType", "vectorIndexConfig"]. - var vectorIndex = new JsonObject(); - vectorIndex.add("vectorIndexType", jsonObject.get("vectorIndexType")); - vectorIndex.add("vectorIndexConfig", jsonObject.get("vectorIndexConfig")); - - var vectorizerObject = jsonObject.get("vectorizer").getAsJsonObject(); - var vectorizerName = vectorizerObject.keySet().iterator().next(); - - Vectorizer.Kind kind; - try { - kind = Vectorizer.Kind.valueOfJson(vectorizerName); - } catch (IllegalArgumentException e) { - return null; - } - - var adapter = delegateAdapters.get(kind); - var concreteVectorizer = vectorizerObject.get(vectorizerName).getAsJsonObject(); - - // Each individual vectorizer has a `VectorIndex vectorIndex` field. - concreteVectorizer.add("vectorIndex", vectorIndex); - - return adapter.fromJsonTree(concreteVectorizer); - } - }.nullSafe(); - } - } -} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/Vectorizers.java b/src/main/java/io/weaviate/client6/v1/api/collections/Vectorizers.java deleted file mode 100644 index 38c9618c6..000000000 --- a/src/main/java/io/weaviate/client6/v1/api/collections/Vectorizers.java +++ /dev/null @@ -1,195 +0,0 @@ -package io.weaviate.client6.v1.api.collections; - -import java.util.Map; -import java.util.function.Function; - -import io.weaviate.client6.v1.api.collections.vectorizers.Img2VecNeuralVectorizer; -import io.weaviate.client6.v1.api.collections.vectorizers.Multi2VecClipVectorizer; -import io.weaviate.client6.v1.api.collections.vectorizers.SelfProvidedVectorizer; -import io.weaviate.client6.v1.api.collections.vectorizers.Text2VecContextionaryVectorizer; -import io.weaviate.client6.v1.api.collections.vectorizers.Text2VecWeaviateVectorizer; -import io.weaviate.client6.v1.internal.ObjectBuilder; - -/** Static factories for creating instances of {@link Vectorizer}. */ -public final class Vectorizers { - /** Prevent public initialization. */ - private Vectorizers() { - } - - /** Create a bring-your-own-vector vector index. */ - public static Map.Entry selfProvided() { - return selfProvided(VectorIndex.DEFAULT_VECTOR_NAME); - } - - /** - * Create a bring-your-own-vector vector index. - * - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry selfProvided( - Function> fn) { - return selfProvided(VectorIndex.DEFAULT_VECTOR_NAME, fn); - } - - /** - * Create a named bring-your-own-vector vector index. - * - * @param vectorName Vector name. - */ - public static Map.Entry selfProvided(String vectorName) { - return Map.entry(vectorName, SelfProvidedVectorizer.of()); - } - - /** - * Create a named bring-your-own-vector vector index. - * - * @param vectorName Vector name. - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry selfProvided(String vectorName, - Function> fn) { - return Map.entry(vectorName, SelfProvidedVectorizer.of(fn)); - } - - /** Create a vector index with an {@code img2vec-neural} vectorizer. */ - public static Map.Entry img2vecNeural() { - return img2vecNeural(VectorIndex.DEFAULT_VECTOR_NAME); - } - - /** - * Create a vector index with an {@code img2vec-neural} vectorizer. - * - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry img2vecNeural( - Function> fn) { - return img2vecNeural(VectorIndex.DEFAULT_VECTOR_NAME, fn); - } - - /** - * Create a named vector index with an {@code img2vec-neural} vectorizer. - * - * @param vectorName Vector name. - */ - public static Map.Entry img2vecNeural(String vectorName) { - return Map.entry(vectorName, Img2VecNeuralVectorizer.of()); - } - - /** - * Create a vector index with an {@code img2vec-neural} vectorizer. - * - * @param vectorName Vector name. - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry img2vecNeural(String vectorName, - Function> fn) { - return Map.entry(vectorName, Img2VecNeuralVectorizer.of(fn)); - } - - /** Create a vector index with an {@code multi2vec-clip} vectorizer. */ - public static Map.Entry multi2vecClip() { - return multi2vecClip(VectorIndex.DEFAULT_VECTOR_NAME); - } - - /** - * Create a vector index with an {@code multi2vec-clip} vectorizer. - * - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry multi2vecClip( - Function> fn) { - return multi2vecClip(VectorIndex.DEFAULT_VECTOR_NAME, fn); - } - - /** - * Create a named vector index with an {@code multi2vec-clip} vectorizer. - * - * @param vectorName Vector name. - */ - public static Map.Entry multi2vecClip(String vectorName) { - return Map.entry(vectorName, Multi2VecClipVectorizer.of()); - } - - /** - * Create a named vector index with an {@code multi2vec-clip} vectorizer. - * - * @param vectorName Vector name. - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry multi2vecClip(String vectorName, - Function> fn) { - return Map.entry(vectorName, Multi2VecClipVectorizer.of(fn)); - } - - /** Create a vector index with an {@code text2vec-contextionary} vectorizer. */ - public static Map.Entry text2vecContextionary() { - return text2vecContextionary(VectorIndex.DEFAULT_VECTOR_NAME); - } - - /** - * Create a vector index with an {@code text2vec-contextionary} vectorizer. - * - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry text2vecContextionary( - Function> fn) { - return text2vecContextionary(VectorIndex.DEFAULT_VECTOR_NAME, fn); - } - - /** - * Create a named vector index with an {@code text2vec-contextionary} - * vectorizer. - * - * @param vectorName Vector name. - */ - public static Map.Entry text2vecContextionary(String vectorName) { - return Map.entry(vectorName, Text2VecContextionaryVectorizer.of()); - } - - /** - * Create a named vector index with an {@code text2vec-contextionary} - * vectorizer. - * - * @param vectorName Vector name. - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry text2vecContextionary(String vectorName, - Function> fn) { - return Map.entry(vectorName, Text2VecContextionaryVectorizer.of(fn)); - } - - /** Create a vector index with an {@code text2vec-weaviate} vectorizer. */ - public static Map.Entry text2VecWeaviate() { - return text2VecWeaviate(VectorIndex.DEFAULT_VECTOR_NAME); - } - - /** - * Create a vector index with an {@code text2vec-weaviate} vectorizer. - * - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry text2VecWeaviate( - Function> fn) { - return text2VecWeaviate(VectorIndex.DEFAULT_VECTOR_NAME, fn); - } - - /** - * Create a named vector index with an {@code text2vec-weaviate} vectorizer. - * - * @param vectorName Vector name. - */ - public static Map.Entry text2VecWeaviate(String vectorName) { - return Map.entry(vectorName, Text2VecWeaviateVectorizer.of()); - } - - /** - * Create a named vector index with an {@code text2vec-weaviate} vectorizer. - * - * @param vectorName Vector name. - * @param fn Lambda expression for optional parameters. - */ - public static Map.Entry text2VecWeaviate(String vectorName, - Function> fn) { - return Map.entry(vectorName, Text2VecWeaviateVectorizer.of(fn)); - } -} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/Vectors.java b/src/main/java/io/weaviate/client6/v1/api/collections/Vectors.java index 97551e6e4..2c5dacf4b 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/Vectors.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/Vectors.java @@ -1,6 +1,7 @@ package io.weaviate.client6.v1.api.collections; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -15,17 +16,13 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import io.weaviate.client6.v1.internal.ObjectBuilder; -import lombok.ToString; - /** * Vectors is an abstraction over named vectors, which can store * both 1-dimensional and 2-dimensional vectors. */ -@ToString public class Vectors { /** Elements of this map must only be {@code float[]} or {@code float[][]}. */ - private final Map namedVectors; + private final Map vectorsMap; /** Create a 1-dimensional vector. */ public static Vectors of(float[] vector) { @@ -58,7 +55,7 @@ public static Vectors of(String name, float[][] vector) { * @param vector {@code float[]} or {@code float[][]} vector. */ private Vectors(String name, Object vector) { - this.namedVectors = Collections.singletonMap(name, vector); + this.vectorsMap = Collections.singletonMap(name, vector); } /** @@ -72,7 +69,7 @@ private Vectors(String name, Object vector) { * @param vector Map of named vectors. */ private Vectors(Map namedVectors) { - this.namedVectors = namedVectors; + this.vectorsMap = namedVectors; } /** Merge all vectors in a single vector map. */ @@ -81,7 +78,7 @@ public Vectors(Vectors... vectors) { for (var vec : vectors) { namedVectors.putAll(vec.asMap()); } - this.namedVectors = namedVectors; + this.vectorsMap = namedVectors; } /** @@ -91,7 +88,7 @@ public Vectors(Vectors... vectors) { * @throws ClassCastException The underlying vector is not a {@code float[]}. */ public float[] getSingle(String name) { - return (float[]) namedVectors.get(name); + return (float[]) vectorsMap.get(name); } /** @@ -112,7 +109,7 @@ public float[] getDefaultSingle() { * {@code float[][]}. */ public float[][] getMulti(String name) { - return (float[][]) namedVectors.get(name); + return (float[][]) vectorsMap.get(name); } /** @@ -134,7 +131,22 @@ public float[][] getDefaultMulti() { * @return Map of name-vector pairs. The returned map is immutable. */ public Map asMap() { - return Map.copyOf(namedVectors); + return Map.copyOf(vectorsMap); + } + + @Override + public String toString() { + var vectorStrings = vectorsMap.entrySet().stream() + .map(v -> { + var name = v.getKey(); + var value = v.getValue(); + var array = (value instanceof float[] f) + ? Arrays.toString((float[]) value) + : Arrays.deepToString((float[][]) value); + return "%s=%s".formatted(name, array); + }) + .toList(); + return "Vectors(%s)".formatted(String.join(", ", vectorStrings)); } public static enum CustomTypeAdapterFactory implements TypeAdapterFactory { @@ -154,7 +166,7 @@ public TypeAdapter create(Gson gson, TypeToken type) { @Override public void write(JsonWriter out, Vectors value) throws IOException { - mapAdapter.write(out, value.namedVectors); + mapAdapter.write(out, value.vectorsMap); } @Override diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/config/UpdateCollectionRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/config/UpdateCollectionRequest.java index e3962e739..f0093492b 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/config/UpdateCollectionRequest.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/config/UpdateCollectionRequest.java @@ -8,9 +8,10 @@ import io.weaviate.client6.v1.api.collections.CollectionConfig; import io.weaviate.client6.v1.api.collections.Generative; import io.weaviate.client6.v1.api.collections.InvertedIndex; +import io.weaviate.client6.v1.api.collections.MultiTenancy; import io.weaviate.client6.v1.api.collections.Replication; import io.weaviate.client6.v1.api.collections.Reranker; -import io.weaviate.client6.v1.api.collections.Vectorizer; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.internal.ObjectBuilder; import io.weaviate.client6.v1.internal.json.JSON; import io.weaviate.client6.v1.internal.rest.Endpoint; @@ -95,14 +96,24 @@ public Builder generativeModule(Generative generativeModule) { return this; } - public final Builder vectors(Map vectors) { - this.newCollection.vectors(vectors); + public final Builder vectorConfig(Map vectors) { + this.newCollection.vectorConfig(vectors); return this; } @SafeVarargs - public final Builder vectors(Map.Entry... vectors) { - this.newCollection.vectors(vectors); + public final Builder vectorConfig(Map.Entry... vectors) { + this.newCollection.vectorConfig(vectors); + return this; + } + + public Builder multiTenancy(MultiTenancy multiTenancy) { + this.newCollection.multiTenancy(multiTenancy); + return this; + } + + public Builder multiTenancy(Function> fn) { + this.newCollection.multiTenancy(fn); return this; } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/config/WeaviateConfigClient.java b/src/main/java/io/weaviate/client6/v1/api/collections/config/WeaviateConfigClient.java index fdc957f01..9fbddea00 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/config/WeaviateConfigClient.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/config/WeaviateConfigClient.java @@ -9,6 +9,7 @@ import io.weaviate.client6.v1.api.collections.CollectionConfig; import io.weaviate.client6.v1.api.collections.CollectionHandleDefaults; import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.ReferenceProperty; import io.weaviate.client6.v1.api.collections.WeaviateCollectionsClient; import io.weaviate.client6.v1.internal.ObjectBuilder; import io.weaviate.client6.v1.internal.grpc.GrpcTransport; @@ -52,7 +53,7 @@ public void addProperty(Property property) throws IOException { } public void addReference(String propertyName, String... dataTypes) throws IOException { - this.addProperty(Property.reference(propertyName, dataTypes).toProperty()); + this.addProperty(ReferenceProperty.to(propertyName, dataTypes).toProperty()); } public void update(String collectionName, diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/config/WeaviateConfigClientAsync.java b/src/main/java/io/weaviate/client6/v1/api/collections/config/WeaviateConfigClientAsync.java index 34d6a66a6..3ace9ee3b 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/config/WeaviateConfigClientAsync.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/config/WeaviateConfigClientAsync.java @@ -10,6 +10,7 @@ import io.weaviate.client6.v1.api.collections.CollectionConfig; import io.weaviate.client6.v1.api.collections.CollectionHandleDefaults; import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.ReferenceProperty; import io.weaviate.client6.v1.api.collections.WeaviateCollectionsClientAsync; import io.weaviate.client6.v1.internal.ObjectBuilder; import io.weaviate.client6.v1.internal.grpc.GrpcTransport; @@ -53,7 +54,7 @@ public CompletableFuture addProperty(Property property) throws IOException } public CompletableFuture addReference(String name, String... dataTypes) throws IOException { - return this.addProperty(Property.reference(name, dataTypes).toProperty()); + return this.addProperty(ReferenceProperty.to(name, dataTypes).toProperty()); } public CompletableFuture update(String collectionName, 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 new file mode 100644 index 000000000..9d4cdb691 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/BQ.java @@ -0,0 +1,62 @@ +package io.weaviate.client6.v1.api.collections.quantizers; + +import java.util.function.Function; + +import com.google.gson.annotations.SerializedName; + +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.internal.ObjectBuilder; + +public record BQ( + @SerializedName("enabled") boolean enabled, + @SerializedName("rescore_limit") Integer rescoreLimit, + @SerializedName("cache") Boolean cache) implements Quantization { + + @Override + public Quantization.Kind _kind() { + return Quantization.Kind.BQ; + } + + @Override + public Object _self() { + return this; + } + + public static BQ of() { + return of(ObjectBuilder.identity()); + } + + public static BQ of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + public BQ(Builder builder) { + this(builder.enabled, builder.rescoreLimit, builder.cache); + } + + public static class Builder implements ObjectBuilder { + private boolean enabled = true; + private Integer rescoreLimit; + private Boolean cache; + + public Builder enabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public Builder rescoreLimit(int rescoreLimit) { + this.rescoreLimit = rescoreLimit; + return this; + } + + public Builder cache(boolean enabled) { + this.cache = enabled; + return this; + } + + @Override + public BQ build() { + return new BQ(this); + } + } +} 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 new file mode 100644 index 000000000..e8caefb43 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/PQ.java @@ -0,0 +1,111 @@ +package io.weaviate.client6.v1.api.collections.quantizers; + +import java.util.function.Function; + +import com.google.gson.annotations.SerializedName; + +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.internal.ObjectBuilder; + +public record PQ( + @SerializedName("enabled") boolean enabled, + @SerializedName("centroids") Integer centroids, + @SerializedName("segments") Integer segments, + @SerializedName("encoder_type") EncoderType encoderType, + @SerializedName("encoder_distribusion") EncoderDistribution encoderDistribution, + @SerializedName("training_limit") Integer trainingLimit, + @SerializedName("bit_compression") Boolean bitCompression) implements Quantization { + + public enum EncoderType { + @SerializedName("kmeans") + KMEANS, + @SerializedName("tile") + TILE; + } + + public enum EncoderDistribution { + @SerializedName("log-normal") + NORMAL, + @SerializedName("normal") + LOG_NORMAL; + } + + @Override + public Quantization.Kind _kind() { + return Quantization.Kind.RQ; + } + + @Override + public Object _self() { + return this; + } + + public static PQ of() { + return of(ObjectBuilder.identity()); + } + + public static PQ of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + public PQ(Builder builder) { + this( + builder.enabled, + builder.centroids, + builder.segments, + builder.encoderType, + builder.encoderDistribution, + builder.trainingLimit, + builder.bitCompression); + } + + public static class Builder implements ObjectBuilder { + private boolean enabled = true; + private Integer centroids; + private Integer segments; + private EncoderType encoderType; + private EncoderDistribution encoderDistribution; + private Integer trainingLimit; + private Boolean bitCompression; + + public Builder enabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public Builder centroids(int centroids) { + this.centroids = centroids; + return this; + } + + public Builder segments(int segments) { + this.segments = segments; + return this; + } + + public Builder encoderType(EncoderType encoderType) { + this.encoderType = encoderType; + return this; + } + + public Builder encoderDistribution(EncoderDistribution encoderDistribution) { + this.encoderDistribution = encoderDistribution; + return this; + } + + public Builder trainingLimit(int trainingLimit) { + this.trainingLimit = trainingLimit; + return this; + } + + public Builder bitCompression(boolean enabled) { + this.bitCompression = enabled; + return this; + } + + @Override + public PQ build() { + return new PQ(this); + } + } +} 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 new file mode 100644 index 000000000..0b4151e08 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/RQ.java @@ -0,0 +1,62 @@ +package io.weaviate.client6.v1.api.collections.quantizers; + +import java.util.function.Function; + +import com.google.gson.annotations.SerializedName; + +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.internal.ObjectBuilder; + +public record RQ( + @SerializedName("enabled") boolean enabled, + @SerializedName("rescore_limit") Integer rescoreLimit, + @SerializedName("bits") Integer bits) implements Quantization { + + @Override + public Quantization.Kind _kind() { + return Quantization.Kind.RQ; + } + + @Override + public Object _self() { + return this; + } + + public static RQ of() { + return of(ObjectBuilder.identity()); + } + + public static RQ of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + public RQ(Builder builder) { + this(builder.enabled, builder.rescoreLimit, builder.bits); + } + + public static class Builder implements ObjectBuilder { + private boolean enabled = true; + private Integer rescoreLimit; + private Integer bits; + + public Builder enabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public Builder rescoreLimit(int rescoreLimit) { + this.rescoreLimit = rescoreLimit; + return this; + } + + public Builder bits(int bits) { + this.bits = bits; + return this; + } + + @Override + public RQ build() { + return new RQ(this); + } + } +} 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 new file mode 100644 index 000000000..ccd9f7070 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/SQ.java @@ -0,0 +1,69 @@ +package io.weaviate.client6.v1.api.collections.quantizers; + +import java.util.function.Function; + +import com.google.gson.annotations.SerializedName; + +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.internal.ObjectBuilder; + +public record SQ( + @SerializedName("enabled") boolean enabled, + @SerializedName("rescore_limit") Integer rescoreLimit, + @SerializedName("training_limit") Integer trainingLimit, + @SerializedName("cache") Boolean cache) implements Quantization { + + @Override + public Quantization.Kind _kind() { + return Quantization.Kind.SQ; + } + + @Override + public Object _self() { + return this; + } + + public static SQ of() { + return of(ObjectBuilder.identity()); + } + + public static SQ of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + public SQ(Builder builder) { + this(builder.enabled, builder.rescoreLimit, builder.trainingLimit, builder.cache); + } + + public static class Builder implements ObjectBuilder { + private boolean enabled = true; + private Integer rescoreLimit; + private Integer trainingLimit; + private Boolean cache; + + public Builder enabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public Builder rescoreLimit(int rescoreLimit) { + this.rescoreLimit = rescoreLimit; + return this; + } + + public Builder trainingLimit(int trainingLimit) { + this.trainingLimit = trainingLimit; + return this; + } + + public Builder cache(boolean enabled) { + this.cache = enabled; + return this; + } + + @Override + public SQ build() { + return new SQ(this); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/Uncompressed.java b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/Uncompressed.java new file mode 100644 index 000000000..ad84ed7c9 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/quantizers/Uncompressed.java @@ -0,0 +1,20 @@ +package io.weaviate.client6.v1.api.collections.quantizers; + +import io.weaviate.client6.v1.api.collections.Quantization; + +public record Uncompressed() implements Quantization { + + @Override + public Quantization.Kind _kind() { + return Quantization.Kind.UNCOMPRESSED; + } + + @Override + public Object _self() { + return true; + } + + public static Uncompressed of() { + return new Uncompressed(); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Img2VecNeuralVectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Img2VecNeuralVectorizer.java index 79cf57867..c5611ef19 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Img2VecNeuralVectorizer.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Img2VecNeuralVectorizer.java @@ -7,19 +7,22 @@ import com.google.gson.annotations.SerializedName; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.VectorIndex; -import io.weaviate.client6.v1.api.collections.Vectorizer; import io.weaviate.client6.v1.internal.ObjectBuilder; public record Img2VecNeuralVectorizer( /** BLOB properties included in the embedding. */ @SerializedName("imageFields") List imageFields, /** Vector index configuration. */ - VectorIndex vectorIndex) implements Vectorizer { + VectorIndex vectorIndex, + /** Vector quantization method. */ + Quantization quantization) implements VectorConfig { @Override - public Vectorizer.Kind _kind() { - return Vectorizer.Kind.IMG2VEC_NEURAL; + public VectorConfig.Kind _kind() { + return VectorConfig.Kind.IMG2VEC_NEURAL; } @Override @@ -36,12 +39,13 @@ public static Img2VecNeuralVectorizer of(Function { private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX; private List imageFields = new ArrayList<>(); + private Quantization quantization; /** Add BLOB properties to include in the embedding. */ public Builder imageFields(List fields) { @@ -66,6 +70,11 @@ public Builder vectorIndex(VectorIndex vectorIndex) { return this; } + public Builder quantization(Quantization quantization) { + this.quantization = quantization; + return this; + } + @Override public Img2VecNeuralVectorizer build() { return new Img2VecNeuralVectorizer(this); diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Multi2VecClipVectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Multi2VecClipVectorizer.java index 6b7fb57a3..07ccfb0ef 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Multi2VecClipVectorizer.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Multi2VecClipVectorizer.java @@ -8,8 +8,9 @@ import com.google.gson.annotations.SerializedName; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.VectorIndex; -import io.weaviate.client6.v1.api.collections.Vectorizer; import io.weaviate.client6.v1.internal.ObjectBuilder; public record Multi2VecClipVectorizer( @@ -22,7 +23,9 @@ public record Multi2VecClipVectorizer( /** Weights of the included properties. */ @SerializedName("weights") Weights weights, /** Vector index configuration. */ - VectorIndex vectorIndex) implements Vectorizer { + VectorIndex vectorIndex, + /** Vector quantization method. */ + Quantization quantization) implements VectorConfig { private static record Weights( /** @@ -38,8 +41,8 @@ private static record Weights( } @Override - public Vectorizer.Kind _kind() { - return Vectorizer.Kind.MULTI2VEC_CLIP; + public VectorConfig.Kind _kind() { + return VectorConfig.Kind.MULTI2VEC_CLIP; } @Override @@ -63,11 +66,13 @@ public Multi2VecClipVectorizer(Builder builder) { new Weights( builder.imageFields.values().stream().toList(), builder.textFields.values().stream().toList()), - builder.vectorIndex); + builder.vectorIndex, + builder.quantization); } public static class Builder implements ObjectBuilder { private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX; + private Quantization quantization; private String inferenceUrl; private Map imageFields = new HashMap<>(); private Map textFields = new HashMap<>(); @@ -134,6 +139,11 @@ public Builder vectorIndex(VectorIndex vectorIndex) { return this; } + public Builder quantization(Quantization quantization) { + this.quantization = quantization; + return this; + } + @Override public Multi2VecClipVectorizer build() { return new Multi2VecClipVectorizer(this); diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/SelfProvidedVectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/SelfProvidedVectorizer.java index 146557227..269053fb0 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/SelfProvidedVectorizer.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/SelfProvidedVectorizer.java @@ -2,15 +2,19 @@ import java.util.function.Function; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.VectorIndex; -import io.weaviate.client6.v1.api.collections.Vectorizer; -import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; import io.weaviate.client6.v1.internal.ObjectBuilder; -public record SelfProvidedVectorizer(VectorIndex vectorIndex) implements Vectorizer { +public record SelfProvidedVectorizer( + /** Vector index configuration. */ + VectorIndex vectorIndex, + /** Vector quantization method. */ + Quantization quantization) implements VectorConfig { @Override public Kind _kind() { - return Vectorizer.Kind.NONE; + return VectorConfig.Kind.NONE; } @Override @@ -27,17 +31,23 @@ public static SelfProvidedVectorizer of(Function { - private VectorIndex vectorIndex = Hnsw.of(); + private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX; + private Quantization quantization; public Builder vectorIndex(VectorIndex vectorIndex) { this.vectorIndex = vectorIndex; return this; } + public Builder quantization(Quantization quantization) { + this.quantization = quantization; + return this; + } + @Override public SelfProvidedVectorizer build() { return new SelfProvidedVectorizer(this); diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecContextionaryVectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecContextionaryVectorizer.java index ad7360ea9..9f7a4808a 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecContextionaryVectorizer.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecContextionaryVectorizer.java @@ -8,8 +8,9 @@ import com.google.gson.annotations.SerializedName; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.VectorIndex; -import io.weaviate.client6.v1.api.collections.Vectorizer; import io.weaviate.client6.v1.internal.ObjectBuilder; public record Text2VecContextionaryVectorizer( @@ -25,11 +26,13 @@ public record Text2VecContextionaryVectorizer( /** Properties included in the embedding. */ @SerializedName("sourceProperties") List sourceProperties, /** Vector index configuration. */ - VectorIndex vectorIndex) implements Vectorizer { + VectorIndex vectorIndex, + /** Vector quantization method. */ + Quantization quantization) implements VectorConfig { @Override - public Vectorizer.Kind _kind() { - return Vectorizer.Kind.TEXT2VEC_CONTEXTIONARY; + public VectorConfig.Kind _kind() { + return VectorConfig.Kind.TEXT2VEC_CONTEXTIONARY; } @Override @@ -50,18 +53,20 @@ public static Text2VecContextionaryVectorizer of( * Canonical constructor always sets {@link #vectorizeCollectionName} to false. */ public Text2VecContextionaryVectorizer(boolean vectorizeCollectionName, List sourceProperties, - VectorIndex vectorIndex) { + VectorIndex vectorIndex, Quantization quantization) { this.vectorizeCollectionName = false; - this.vectorIndex = vectorIndex; this.sourceProperties = Collections.emptyList(); + this.vectorIndex = vectorIndex; + this.quantization = quantization; } public Text2VecContextionaryVectorizer(Builder builder) { - this(builder.vectorizeCollectionName, builder.sourceProperties, builder.vectorIndex); + this(builder.vectorizeCollectionName, builder.sourceProperties, builder.vectorIndex, builder.quantization); } public static class Builder implements ObjectBuilder { private final boolean vectorizeCollectionName = false; + private Quantization quantization; private List sourceProperties = new ArrayList<>(); private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX; @@ -89,6 +94,11 @@ public Builder vectorIndex(VectorIndex vectorIndex) { return this; } + public Builder quantization(Quantization quantization) { + this.quantization = quantization; + return this; + } + public Text2VecContextionaryVectorizer build() { return new Text2VecContextionaryVectorizer(this); } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecWeaviateVectorizer.java b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecWeaviateVectorizer.java index 00ff9db7d..629befb73 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecWeaviateVectorizer.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/vectorizers/Text2VecWeaviateVectorizer.java @@ -7,8 +7,9 @@ import com.google.gson.annotations.SerializedName; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.VectorIndex; -import io.weaviate.client6.v1.api.collections.Vectorizer; import io.weaviate.client6.v1.internal.ObjectBuilder; public record Text2VecWeaviateVectorizer( @@ -21,11 +22,13 @@ public record Text2VecWeaviateVectorizer( /** Properties included in the embedding. */ @SerializedName("sourceProperties") List sourceProperties, /** Vector index configuration. */ - VectorIndex vectorIndex) implements Vectorizer { + VectorIndex vectorIndex, + /** Vector quantization method. */ + Quantization quantization) implements VectorConfig { @Override - public Vectorizer.Kind _kind() { - return Vectorizer.Kind.TEXT2VEC_WEAVIATE; + public VectorConfig.Kind _kind() { + return VectorConfig.Kind.TEXT2VEC_WEAVIATE; } @Override @@ -47,7 +50,8 @@ public Text2VecWeaviateVectorizer(Builder builder) { builder.dimensions, builder.model, builder.sourceProperties, - builder.vectorIndex); + builder.vectorIndex, + builder.quantization); } public static final String SNOWFLAKE_ARCTIC_EMBED_M_15 = "Snowflake/snowflake-arctic-embed-m-v1.5"; @@ -55,6 +59,7 @@ public Text2VecWeaviateVectorizer(Builder builder) { public static class Builder implements ObjectBuilder { private VectorIndex vectorIndex = VectorIndex.DEFAULT_VECTOR_INDEX; + private Quantization quantization; private String inferenceUrl; private Integer dimensions; private String model; @@ -110,6 +115,11 @@ public Builder vectorIndex(VectorIndex vectorIndex) { return this; } + public Builder quantization(Quantization quantization) { + this.quantization = quantization; + return this; + } + public Text2VecWeaviateVectorizer build() { return new Text2VecWeaviateVectorizer(this); } diff --git a/src/main/java/io/weaviate/client6/v1/internal/TransportOptions.java b/src/main/java/io/weaviate/client6/v1/internal/TransportOptions.java index 8700bee47..60d3db0d5 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/TransportOptions.java +++ b/src/main/java/io/weaviate/client6/v1/internal/TransportOptions.java @@ -4,12 +4,12 @@ import javax.net.ssl.TrustManagerFactory; public abstract class TransportOptions { - private final String scheme; - private final String host; - private final int port; - private final TokenProvider tokenProvider; - private final H headers; - private final TrustManagerFactory trustManagerFactory; + protected final String scheme; + protected final String host; + protected final int port; + protected final TokenProvider tokenProvider; + protected final H headers; + protected final TrustManagerFactory trustManagerFactory; protected TransportOptions(String scheme, String host, int port, H headers, TokenProvider tokenProvider, TrustManagerFactory tmf) { diff --git a/src/main/java/io/weaviate/client6/v1/internal/grpc/DefaultGrpcTransport.java b/src/main/java/io/weaviate/client6/v1/internal/grpc/DefaultGrpcTransport.java index a9a50c200..bb4a8aa88 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/grpc/DefaultGrpcTransport.java +++ b/src/main/java/io/weaviate/client6/v1/internal/grpc/DefaultGrpcTransport.java @@ -36,6 +36,12 @@ public DefaultGrpcTransport(GrpcChannelOptions transportOptions) { var futureStub = WeaviateGrpc.newFutureStub(channel) .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(transportOptions.headers())); + if (transportOptions.maxMessageSize() != null) { + var max = transportOptions.maxMessageSize(); + blockingStub.withMaxInboundMessageSize(max).withMaxOutboundMessageSize(max); + futureStub.withMaxInboundMessageSize(max).withMaxOutboundMessageSize(max); + } + if (transportOptions.tokenProvider() != null) { this.callCredentials = new TokenCallCredentials(transportOptions.tokenProvider()); blockingStub = blockingStub.withCallCredentials(callCredentials); diff --git a/src/main/java/io/weaviate/client6/v1/internal/grpc/GrpcChannelOptions.java b/src/main/java/io/weaviate/client6/v1/internal/grpc/GrpcChannelOptions.java index 6e32d9738..e59893412 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/grpc/GrpcChannelOptions.java +++ b/src/main/java/io/weaviate/client6/v1/internal/grpc/GrpcChannelOptions.java @@ -9,9 +9,25 @@ import io.weaviate.client6.v1.internal.TransportOptions; public class GrpcChannelOptions extends TransportOptions { + private final Integer maxMessageSize; + public GrpcChannelOptions(String scheme, String host, int port, Map headers, TokenProvider tokenProvider, TrustManagerFactory tmf) { - super(scheme, host, port, buildMetadata(headers), tokenProvider, tmf); + this(scheme, host, port, buildMetadata(headers), tokenProvider, tmf, null); + } + + private GrpcChannelOptions(String scheme, String host, int port, Metadata headers, + TokenProvider tokenProvider, TrustManagerFactory tmf, Integer maxMessageSize) { + super(scheme, host, port, headers, tokenProvider, tmf); + this.maxMessageSize = maxMessageSize; + } + + public GrpcChannelOptions withMaxMessageSize(int maxMessageSize) { + return new GrpcChannelOptions(scheme, host, port, headers, tokenProvider, trustManagerFactory, maxMessageSize); + } + + public Integer maxMessageSize() { + return maxMessageSize; } private static final Metadata buildMetadata(Map headers) { diff --git a/src/main/java/io/weaviate/client6/v1/internal/json/JSON.java b/src/main/java/io/weaviate/client6/v1/internal/json/JSON.java index a56fce609..d800b2f48 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/json/JSON.java +++ b/src/main/java/io/weaviate/client6/v1/internal/json/JSON.java @@ -20,13 +20,15 @@ public final class JSON { gsonBuilder.registerTypeAdapterFactory( io.weaviate.client6.v1.api.collections.Vectors.CustomTypeAdapterFactory.INSTANCE); gsonBuilder.registerTypeAdapterFactory( - io.weaviate.client6.v1.api.collections.Vectorizer.CustomTypeAdapterFactory.INSTANCE); + io.weaviate.client6.v1.api.collections.VectorConfig.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( io.weaviate.client6.v1.api.collections.Generative.CustomTypeAdapterFactory.INSTANCE); + gsonBuilder.registerTypeAdapterFactory( + io.weaviate.client6.v1.api.collections.Quantization.CustomTypeAdapterFactory.INSTANCE); gsonBuilder.registerTypeAdapterFactory( io.weaviate.client6.v1.internal.DateUtil.CustomTypeAdapterFactory.INSTANCE); diff --git a/src/main/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransport.java b/src/main/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransport.java index 66e1f7862..2e6323be9 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransport.java +++ b/src/main/java/io/weaviate/client6/v1/internal/rest/DefaultRestTransport.java @@ -28,6 +28,7 @@ import org.apache.hc.core5.io.CloseMode; import io.weaviate.client6.v1.api.WeaviateApiException; +import io.weaviate.client6.v1.api.WeaviateTransportException; public class DefaultRestTransport implements RestTransport { private final CloseableHttpClient httpClient; @@ -53,8 +54,7 @@ public DefaultRestTransport(RestTransportOptions transportOptions) { sslCtx.init(null, transportOptions.trustManagerFactory().getTrustManagers(), null); tlsStrategy = new DefaultClientTlsStrategy(sslCtx); } catch (NoSuchAlgorithmException | KeyManagementException e) { - // todo: throw WeaviateConnectionException - throw new RuntimeException("connect to Weaviate", e); + throw new WeaviateTransportException("init custom SSL context", e); } PoolingHttpClientConnectionManager syncManager = PoolingHttpClientConnectionManagerBuilder.create() @@ -96,7 +96,6 @@ private ClassicHttpRequest prepareClassicRequest(RequestT var method = endpoint.method(request); var uri = uri(endpoint, request); - // TODO: apply options; var req = ClassicRequestBuilder.create(method).setUri(uri); var body = endpoint.body(request); if (body != null) { @@ -180,8 +179,7 @@ private ResponseT _handleResponse(Endpoint endpoint, S return (ResponseT) ((Boolean) bool.getResult(statusCode)); } - // TODO: make it a WeaviateTransportException - throw new RuntimeException("Unhandled endpoint type " + endpoint.getClass().getSimpleName()); + throw new WeaviateTransportException("Unhandled endpoint type " + endpoint.getClass().getSimpleName()); } @Override diff --git a/src/test/java/io/weaviate/client6/v1/api/WeaviateClientAsyncTest.java b/src/test/java/io/weaviate/client6/v1/api/WeaviateClientAsyncTest.java new file mode 100644 index 000000000..e538de7a0 --- /dev/null +++ b/src/test/java/io/weaviate/client6/v1/api/WeaviateClientAsyncTest.java @@ -0,0 +1,29 @@ +package io.weaviate.client6.v1.api; + +import org.junit.Test; + +public class WeaviateClientAsyncTest { + + @SuppressWarnings("resource") + @Test(expected = WeaviateConnectException.class) + public void testFailedConnection() { + var config = new Config.Local(); + config.host("localhost").port(1234); + new WeaviateClientAsync(config.build()); + } + + @Test(expected = WeaviateConnectException.class) + public void testFailedConnection_Local() { + WeaviateClientAsync.connectToLocal(); + } + + @Test(expected = WeaviateConnectException.class) + public void testFailedConnection_WeaviateCloud() { + WeaviateClientAsync.connectToWeaviateCloud("no-cluster.io", "no-key"); + } + + @Test(expected = WeaviateConnectException.class) + public void testFailedConnection_Custom() { + WeaviateClient.connectToCustom(conn -> conn.httpHost("localhost").httpPort(1234)); + } +} diff --git a/src/test/java/io/weaviate/client6/v1/api/WeaviateClientTest.java b/src/test/java/io/weaviate/client6/v1/api/WeaviateClientTest.java new file mode 100644 index 000000000..e51bd66c3 --- /dev/null +++ b/src/test/java/io/weaviate/client6/v1/api/WeaviateClientTest.java @@ -0,0 +1,29 @@ +package io.weaviate.client6.v1.api; + +import org.junit.Test; + +public class WeaviateClientTest { + + @SuppressWarnings("resource") + @Test(expected = WeaviateConnectException.class) + public void testFailedConnection() { + var config = new Config.Local(); + config.host("localhost").port(1234); + new WeaviateClient(config.build()); + } + + @Test(expected = WeaviateConnectException.class) + public void testFailedConnection_Local() { + WeaviateClient.connectToLocal(); + } + + @Test(expected = WeaviateConnectException.class) + public void testFailedConnection_WeaviateCloud() { + WeaviateClient.connectToWeaviateCloud("no-cluster.io", "no-key"); + } + + @Test(expected = WeaviateConnectException.class) + public void testFailedConnection_Custom() { + WeaviateClient.connectToCustom(conn -> conn.httpHost("localhost").httpPort(1234)); + } +} diff --git a/src/test/java/io/weaviate/client6/v1/api/collections/VectorsTest.java b/src/test/java/io/weaviate/client6/v1/api/collections/VectorsTest.java new file mode 100644 index 000000000..817d79b15 --- /dev/null +++ b/src/test/java/io/weaviate/client6/v1/api/collections/VectorsTest.java @@ -0,0 +1,29 @@ +package io.weaviate.client6.v1.api.collections; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +public class VectorsTest { + @Test + public void testToString_1d() { + var vector = Vectors.of(new float[] { 1, 2, 3 }); + var got = vector.toString(); + Assertions.assertThat(got).isEqualTo("Vectors(default=[1.0, 2.0, 3.0])"); + } + + @Test + public void testToString_2d() { + var vector = Vectors.of(new float[][] { { 1, 2, 3 }, { 1, 2, 3 } }); + var got = vector.toString(); + Assertions.assertThat(got).isEqualTo("Vectors(default=[[1.0, 2.0, 3.0], [1.0, 2.0, 3.0]])"); + } + + @Test + public void testToString_multiple() { + var title = Vectors.of("title", new float[] { 1, 2, 3 }); + var body = Vectors.of("body", new float[][] { { 1, 2, 3 }, { 1, 2, 3 } }); + var vectors = new Vectors(title, body); + var got = vectors.toString(); + Assertions.assertThat(got).isEqualTo("Vectors(title=[1.0, 2.0, 3.0], body=[[1.0, 2.0, 3.0], [1.0, 2.0, 3.0]])"); + } +} 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 e045ad162..9a6825945 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 @@ -18,10 +18,11 @@ import io.weaviate.client6.v1.api.collections.Generative; import io.weaviate.client6.v1.api.collections.ObjectMetadata; import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.ReferenceProperty; import io.weaviate.client6.v1.api.collections.Reranker; import io.weaviate.client6.v1.api.collections.Tokenization; -import io.weaviate.client6.v1.api.collections.Vectorizer; -import io.weaviate.client6.v1.api.collections.Vectorizers; +import io.weaviate.client6.v1.api.collections.VectorConfig; import io.weaviate.client6.v1.api.collections.Vectors; import io.weaviate.client6.v1.api.collections.WeaviateObject; import io.weaviate.client6.v1.api.collections.data.BatchReference; @@ -44,7 +45,7 @@ public static Object[][] testCases() { return new Object[][] { // Vectorizer.CustomTypeAdapterFactory { - Vectorizer.class, + VectorConfig.class, SelfProvidedVectorizer.of(), """ { @@ -55,7 +56,7 @@ public static Object[][] testCases() { """, }, { - Vectorizer.class, + VectorConfig.class, Img2VecNeuralVectorizer.of(i2v -> i2v.imageFields("jpeg", "png")), """ { @@ -70,7 +71,7 @@ public static Object[][] testCases() { """, }, { - Vectorizer.class, + VectorConfig.class, Multi2VecClipVectorizer.of(m2v -> m2v .inferenceUrl("http://example.com") .imageField("img", 1f) @@ -94,7 +95,7 @@ public static Object[][] testCases() { """, }, { - Vectorizer.class, + VectorConfig.class, Text2VecContextionaryVectorizer.of(), """ { @@ -110,7 +111,7 @@ public static Object[][] testCases() { """, }, { - Vectorizer.class, + VectorConfig.class, Text2VecWeaviateVectorizer.of(t2v -> t2v .inferenceUrl("http://example.com") .dimensions(4) @@ -134,7 +135,7 @@ public static Object[][] testCases() { // VectorIndex.CustomTypeAdapterFactory { - Vectorizer.class, + VectorConfig.class, SelfProvidedVectorizer.of(none -> none .vectorIndex(Flat.of(flat -> flat .vectorCacheMaxObjects(100)))), @@ -147,7 +148,41 @@ public static Object[][] testCases() { """, }, { - Vectorizer.class, + VectorConfig.class, + SelfProvidedVectorizer.of(none -> none + .quantization(Quantization.bq(bq -> bq + .rescoreLimit(10) + .cache(true)))), + """ + { + "vectorIndexType": "hnsw", + "vectorizer": {"none": {}}, + "vectorIndexConfig": { + "bq": { + "enabled": true, + "rescore_limit": 10, + "cache": true + } + } + } + """, + }, + { + VectorConfig.class, + SelfProvidedVectorizer.of(none -> none + .quantization(Quantization.uncompressed())), + """ + { + "vectorIndexType": "hnsw", + "vectorizer": {"none": {}}, + "vectorIndexConfig": { + "skipDefaultQuantization": true + } + } + """, + }, + { + VectorConfig.class, SelfProvidedVectorizer.of(none -> none .vectorIndex(Hnsw.of(hnsw -> hnsw .distance(Distance.DOT) @@ -228,9 +263,9 @@ public static Object[][] testCases() { Property.text("custom_id", p -> p.tokenization(Tokenization.WORD)), Property.integer("size")) .references( - Property.reference("owner", "Person", "Company")) - .vectors( - Vectorizers.img2vecNeural("v-shape", + ReferenceProperty.to("owner", "Person", "Company")) + .vectorConfig( + VectorConfig.img2vecNeural("v-shape", i2v -> i2v.imageFields("img")))), """ {