From db4ea3a34c05575a6c4ee6d24c6233d7557e4ea4 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sat, 8 Mar 2025 21:43:27 +0100 Subject: [PATCH 01/17] feat: create, get, and delete objects Added simplified collection creation with only 1 vector index configuration ('none'). --- pom.xml | 14 ++- .../weaviate/client6/v1/CollectionsITest.java | 5 + .../io/weaviate/client6/v1/DataITest.java | 75 +++++++++++++ .../io/weaviate/containers/Container.java | 96 +++++++++++++++++ .../io/weaviate/containers/Contextionary.java | 46 ++++++++ .../java/io/weaviate/containers/Weaviate.java | 91 ++++++++++++++++ src/main/java/io/weaviate/client6/Config.java | 16 +++ .../io/weaviate/client6/WeaviateClient.java | 15 +++ .../weaviate/client6/internal/HttpClient.java | 4 + .../io/weaviate/client6/v1/Collection.java | 15 +++ .../io/weaviate/client6/v1/Collections.java | 54 ++++++++++ .../v1/collections/CollectionDefinition.java | 53 +++++++++ .../client6/v1/collections/DataType.java | 10 ++ .../v1/collections/NoneVectorIndex.java | 12 +++ .../client6/v1/collections/Property.java | 45 ++++++++ .../dto/CollectionDefinitionDTO.java | 35 ++++++ .../client6/v1/data/CustomMetadata.java | 28 +++++ .../io/weaviate/client6/v1/data/Data.java | 102 ++++++++++++++++++ .../io/weaviate/client6/v1/data/Vectors.java | 86 +++++++++++++++ .../client6/v1/data/WeaviateObject.java | 63 +++++++++++ 20 files changed, 864 insertions(+), 1 deletion(-) create mode 100644 src/it/java/io/weaviate/client6/v1/CollectionsITest.java create mode 100644 src/it/java/io/weaviate/client6/v1/DataITest.java create mode 100644 src/it/java/io/weaviate/containers/Container.java create mode 100644 src/it/java/io/weaviate/containers/Contextionary.java create mode 100644 src/it/java/io/weaviate/containers/Weaviate.java create mode 100644 src/main/java/io/weaviate/client6/Config.java create mode 100644 src/main/java/io/weaviate/client6/WeaviateClient.java create mode 100644 src/main/java/io/weaviate/client6/internal/HttpClient.java create mode 100644 src/main/java/io/weaviate/client6/v1/Collection.java create mode 100644 src/main/java/io/weaviate/client6/v1/Collections.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/DataType.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/NoneVectorIndex.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/Property.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/dto/CollectionDefinitionDTO.java create mode 100644 src/main/java/io/weaviate/client6/v1/data/CustomMetadata.java create mode 100644 src/main/java/io/weaviate/client6/v1/data/Data.java create mode 100644 src/main/java/io/weaviate/client6/v1/data/Vectors.java create mode 100644 src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java diff --git a/pom.xml b/pom.xml index 393474d64..98283db29 100644 --- a/pom.xml +++ b/pom.xml @@ -334,12 +334,24 @@ + + add-test-source + process-resources + + add-test-source + + + + ${project.basedir}/src/it/java + + + org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.11.2 attach-javadocs diff --git a/src/it/java/io/weaviate/client6/v1/CollectionsITest.java b/src/it/java/io/weaviate/client6/v1/CollectionsITest.java new file mode 100644 index 000000000..d83847693 --- /dev/null +++ b/src/it/java/io/weaviate/client6/v1/CollectionsITest.java @@ -0,0 +1,5 @@ +package io.weaviate.client6.v1; + +public class CollectionsITest { + +} diff --git a/src/it/java/io/weaviate/client6/v1/DataITest.java b/src/it/java/io/weaviate/client6/v1/DataITest.java new file mode 100644 index 000000000..b056598e7 --- /dev/null +++ b/src/it/java/io/weaviate/client6/v1/DataITest.java @@ -0,0 +1,75 @@ +package io.weaviate.client6.v1; + +import java.io.IOException; +import java.util.Map; +import java.util.UUID; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.weaviate.client6.WeaviateClient; +import io.weaviate.client6.v1.collections.Property; +import io.weaviate.client6.v1.data.Vectors; +import io.weaviate.containers.Container; + +public class DataITest { + // public static final Container.Group compose = Container.compose( + // Weaviate.custom() + // .withDefaultVectorizer(Contextionary.MODULE) + // .withContextionaryUrl(Contextionary.URL) + // .build(), + // Container.CONTEXTIONARY); + + private static WeaviateClient client = Container.WEAVIATE.getClient(); + + // @ClassRule + // public static TestRule _rule = compose.asTestRule(); + + private static final String COLLECTION = "Things"; + + @BeforeClass + public static void beforeAll() throws IOException { + // client = compose.getClient(); + createTestCollection(); + } + + @Test + public void testCreateGetDelete() throws IOException { + var things = client.collections.use(COLLECTION); + var id = UUID.randomUUID().toString(); + Float[] vector = { 1f, 2f, 3f }; + + things.data.insert(Map.of("username", "john doe"), metadata -> metadata + .id(id) + .vectors(Vectors.of("bring_your_own", vector))); + + var object = things.data.get(id); + Assertions.assertThat(object) + .as("object exists after insert").get() + .satisfies(obj -> { + Assertions.assertThat(obj.metadata.id) + .as("object id").isEqualTo(id); + + Assertions.assertThat(obj.metadata.vectors).extracting(Vectors::getSingle) + .asInstanceOf(InstanceOfAssertFactories.OPTIONAL).as("has single vector").get() + .asInstanceOf(InstanceOfAssertFactories.array(Float[].class)).containsExactly(vector); + + Assertions.assertThat(obj.properties) + .as("has expected properties") + .containsEntry("username", "john doe"); + }); + + things.data.delete(id); + object = things.data.get(id); + Assertions.assertThat(object).isEmpty().as("object not exists after deletion"); + } + + private static void createTestCollection() throws IOException { + client.collections.create(COLLECTION, + col -> col + .properties(Property.text("username")) + .vector("bring_your_own")); + } +} diff --git a/src/it/java/io/weaviate/containers/Container.java b/src/it/java/io/weaviate/containers/Container.java new file mode 100644 index 000000000..d74880981 --- /dev/null +++ b/src/it/java/io/weaviate/containers/Container.java @@ -0,0 +1,96 @@ +package io.weaviate.containers; + +import java.util.Arrays; +import java.util.List; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.lifecycle.Startable; + +import io.weaviate.client6.WeaviateClient; +import lombok.RequiredArgsConstructor; + +public class Container { + public static final Weaviate WEAVIATE = Weaviate.createDefault(); + public static final Contextionary CONTEXTIONARY = Contextionary.createDefault(); + + static { + WEAVIATE.start(); + } + + public static Group compose(Weaviate weaviate, GenericContainer... containers) { + return new Group(weaviate, containers); + } + + public static TestRule asTestRule(Startable container) { + System.out.print("HERE"); + return new PerTestSuite(container); + }; + + public static class Group implements Startable { + private final Weaviate weaviate; + private final List> containers; + + private Group(Weaviate weaviate, GenericContainer... containers) { + this.weaviate = weaviate; + this.containers = Arrays.asList(containers); + setSharedNetwork(); + System.out.println("Group initialized"); + } + + public WeaviateClient getClient() { + System.out.println("get Weaviate client"); + return weaviate.getClient(); + } + + @Override + public void start() { + System.out.println("Starting containers..."); + containers.forEach(GenericContainer::start); + System.out.println("Starting Weaviate..."); + weaviate.start(); + System.out.println("Started"); + } + + @Override + public void stop() { + System.out.println("Stopping..."); + weaviate.stop(); + containers.forEach(GenericContainer::stop); + } + + private void setSharedNetwork() { + System.out.println("Set shared network..."); + weaviate.setNetwork(Network.SHARED); + containers.forEach(c -> c.setNetwork(Network.SHARED)); + } + + public TestRule asTestRule() { + System.out.println("As TestRule!"); + return new PerTestSuite(this); + }; + } + + @RequiredArgsConstructor + public static class PerTestSuite implements TestRule { + private final Startable container; + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + container.start(); + base.evaluate(); + } finally { + container.stop(); + } + } + }; + } + } +} diff --git a/src/it/java/io/weaviate/containers/Contextionary.java b/src/it/java/io/weaviate/containers/Contextionary.java new file mode 100644 index 000000000..76ec5aefd --- /dev/null +++ b/src/it/java/io/weaviate/containers/Contextionary.java @@ -0,0 +1,46 @@ +package io.weaviate.containers; + +import org.testcontainers.containers.GenericContainer; + +public class Contextionary extends GenericContainer { + public static final String VERSION = "en0.16.0-v1.2.1"; + public static final String DOCKER_IMAGE = "semitechnologies/contextionary"; + public static final String MODULE = "text2vec-contextionary"; + + public static final String HOST_NAME = "contextionary"; + public static final String URL = HOST_NAME + ":9999"; + + static Contextionary createDefault() { + return new Builder().build(); + } + + static Contextionary.Builder custom() { + return new Builder(); + } + + public static class Builder { + private String versionTag; + + public Builder() { + this.versionTag = VERSION; + } + + public Contextionary build() { + var container = new Contextionary(DOCKER_IMAGE + ":" + versionTag); + container + .withEnv("OCCURRENCE_WEIGHT_LINEAR_FACTOR", "true") + .withEnv("PERSISTENCE_DATA_PATH", "/var/lib/weaviate") + .withEnv("OCCURRENCE_WEIGHT_LINEAR_FACTOR", "0.75") + .withEnv("EXTENSIONS_STORAGE_MODE", "weaviate") + .withEnv("EXTENSIONS_STORAGE_ORIGIN", "http://weaviate:8080") + .withEnv("NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE", "5") + .withEnv("ENABLE_COMPOUND_SPLITTING", "'false'"); + container.withCreateContainerCmdModifier(cmd -> cmd.withHostName("contextionary")); + return container; + } + } + + public Contextionary(String image) { + super(image); + } +} diff --git a/src/it/java/io/weaviate/containers/Weaviate.java b/src/it/java/io/weaviate/containers/Weaviate.java new file mode 100644 index 000000000..1c587e305 --- /dev/null +++ b/src/it/java/io/weaviate/containers/Weaviate.java @@ -0,0 +1,91 @@ +package io.weaviate.containers; + +import java.util.HashSet; +import java.util.Set; + +import org.testcontainers.weaviate.WeaviateContainer; + +import io.weaviate.client6.Config; +import io.weaviate.client6.WeaviateClient; + +public class Weaviate extends WeaviateContainer { + public static final String VERSION = "1.29.0"; + public static final String DOCKER_IMAGE = "semitechnologies/weaviate"; + + public WeaviateClient getClient() { + var config = new Config("http", getHttpHostAddress()); + return new WeaviateClient(config); + } + + public static Weaviate createDefault() { + return new Builder().build(); + } + + public static Weaviate.Builder custom() { + return new Builder(); + } + + public static class Builder { + private String versionTag; + private Set enableModules; + private String defaultVectorizerModule; + private String contextionaryUrl; + private boolean telemetry; + + public Builder() { + this.versionTag = VERSION; + this.enableModules = new HashSet<>(); + this.telemetry = false; + } + + public Builder withVersion(String version) { + this.versionTag = version; + return this; + } + + public Builder addModule(String module) { + enableModules.add(module); + return this; + } + + public Builder withDefaultVectorizer(String module) { + addModule(module); + defaultVectorizerModule = module; + return this; + } + + public Builder withContextionaryUrl(String url) { + contextionaryUrl = url; + return this; + } + + public Builder enableTelemetry() { + telemetry = true; + return this; + } + + public Weaviate build() { + var c = new Weaviate(DOCKER_IMAGE + ":" + versionTag); + + if (!enableModules.isEmpty()) { + c.withEnv("ENABLE_MODULES", String.join(",", enableModules)); + } + if (defaultVectorizerModule != null) { + c.withEnv("DEFAULT_VECTORIZER_MODULE", defaultVectorizerModule); + } + if (contextionaryUrl != null) { + c.withEnv("CONTEXTIONARY_URL", contextionaryUrl); + } + if (!telemetry) { + c.withEnv("DISABLE_TELEMETRY", "true"); + } + + c.withCreateContainerCmdModifier(cmd -> cmd.withHostName("weaviate")); + return c; + } + } + + private Weaviate(String dockerImageName) { + super(dockerImageName); + } +} diff --git a/src/main/java/io/weaviate/client6/Config.java b/src/main/java/io/weaviate/client6/Config.java new file mode 100644 index 000000000..dafe90077 --- /dev/null +++ b/src/main/java/io/weaviate/client6/Config.java @@ -0,0 +1,16 @@ +package io.weaviate.client6; + +public class Config { + private final String version = "v1"; + private final String scheme; + private final String host; + + public Config(String scheme, String host) { + this.scheme = scheme; + this.host = host; + } + + public String baseUrl() { + return scheme + "://" + host + "/" + version; + } +} diff --git a/src/main/java/io/weaviate/client6/WeaviateClient.java b/src/main/java/io/weaviate/client6/WeaviateClient.java new file mode 100644 index 000000000..e9b6cc06f --- /dev/null +++ b/src/main/java/io/weaviate/client6/WeaviateClient.java @@ -0,0 +1,15 @@ +package io.weaviate.client6; + +import io.weaviate.client6.v1.Collections; + +public class WeaviateClient { + public final Collections collections; + + // TODO: hide befind an internal HttpClient + private final Config config; + + public WeaviateClient(Config config) { + this.config = config; + this.collections = new Collections(config); + } +} diff --git a/src/main/java/io/weaviate/client6/internal/HttpClient.java b/src/main/java/io/weaviate/client6/internal/HttpClient.java new file mode 100644 index 000000000..5a25d406a --- /dev/null +++ b/src/main/java/io/weaviate/client6/internal/HttpClient.java @@ -0,0 +1,4 @@ +package io.weaviate.client6.internal; + +public class HttpClient { +} diff --git a/src/main/java/io/weaviate/client6/v1/Collection.java b/src/main/java/io/weaviate/client6/v1/Collection.java new file mode 100644 index 000000000..dde111118 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/Collection.java @@ -0,0 +1,15 @@ +package io.weaviate.client6.v1; + +import io.weaviate.client6.Config; +import io.weaviate.client6.v1.data.Data; +import io.weaviate.client6.v1.query.Query; + +public class Collection { + public final Query query; + public final Data data; + + public Collection(Config config, String collectionName) { + this.query = new Query<>(); + this.data = new Data<>(collectionName, config); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/Collections.java b/src/main/java/io/weaviate/client6/v1/Collections.java new file mode 100644 index 000000000..e9aecb34c --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/Collections.java @@ -0,0 +1,54 @@ +package io.weaviate.client6.v1; + +import java.io.IOException; +import java.util.Map; +import java.util.function.Consumer; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; + +import com.google.gson.Gson; + +import io.weaviate.client6.Config; +import io.weaviate.client6.v1.collections.CollectionDefinition; +import io.weaviate.client6.v1.collections.dto.CollectionDefinitionDTO; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class Collections { + // TODO: hide befind an internal HttpClient + private final Config config; + + private static final Gson gson = new Gson(); + + public void create(String name, Consumer options) throws IOException { + var collection = new CollectionDefinition(name, options); + var body = new CollectionDefinitionDTO(collection); + + try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + String jsonBody = gson.toJson(body); + ClassicHttpRequest httpPost = ClassicRequestBuilder + .post(config.baseUrl() + "/schema") + .setEntity(jsonBody, ContentType.APPLICATION_JSON) + .build(); + + httpclient.execute(httpPost, response -> { + var entity = response.getEntity(); + if (response.getCode() != HttpStatus.SC_SUCCESS) { // Does not return 201 + var message = EntityUtils.toString(entity); + throw new RuntimeException("HTTP " + response.getCode() + ": " + message); + } + return null; + }); + } + } + + public Collection> use(String name) { + return new Collection<>(config, name); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java new file mode 100644 index 000000000..b5254e90b --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java @@ -0,0 +1,53 @@ +package io.weaviate.client6.v1.collections; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class CollectionDefinition { + public final String name; + public final List properties; + public final Map vectorConfig; + + public CollectionDefinition(String name, Consumer options) { + var config = new Configuration(options); + + this.name = name; + this.properties = config.properties; + this.vectorConfig = config.vectorConfig; + } + + public interface VectorConfig { + Object vectorizer(); + + String indexType(); + + Object indexConfiguration(); + } + + // Tucked Builder for additional collection configuration. + public static class Configuration { + public List properties; + public final Map vectorConfig; + + public Configuration properties(Property... properties) { + this.properties = Arrays.asList(properties); + return this; + } + + // By default, we configure a "none" vectorizer. + public Configuration vector(String name) { + this.vectorConfig.put(name, new NoneVectorIndex()); + return this; + } + + Configuration(Consumer options) { + this.properties = new ArrayList<>(); + this.vectorConfig = new HashMap<>(); + options.accept(this); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/DataType.java b/src/main/java/io/weaviate/client6/v1/collections/DataType.java new file mode 100644 index 000000000..8ec96470f --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/DataType.java @@ -0,0 +1,10 @@ +package io.weaviate.client6.v1.collections; + +import com.google.gson.annotations.SerializedName; + +public enum DataType { + @SerializedName("text") + TEXT, + @SerializedName("int") + INT; +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/NoneVectorIndex.java b/src/main/java/io/weaviate/client6/v1/collections/NoneVectorIndex.java new file mode 100644 index 000000000..83518497a --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/NoneVectorIndex.java @@ -0,0 +1,12 @@ +package io.weaviate.client6.v1.collections; + +import java.util.HashMap; +import java.util.Map; + +public record NoneVectorIndex(Object vectorizer, String indexType, Object indexConfiguration) + implements CollectionDefinition.VectorConfig { + + public NoneVectorIndex() { + this(Map.of("none", new Object()), "flat", new HashMap<>()); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/Property.java b/src/main/java/io/weaviate/client6/v1/collections/Property.java new file mode 100644 index 000000000..a0b6b88d7 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/Property.java @@ -0,0 +1,45 @@ +package io.weaviate.client6.v1.collections; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import com.google.gson.annotations.SerializedName; + +public class Property { + @SerializedName("name") + public final String name; + + @SerializedName("dataType") + public final List dataTypes; + + public static Property text(String name) { + return new Property(name, DataType.TEXT); + } + + public static Property integer(String name) { + return new Property(name, DataType.INT); + } + + public static final class Configuration { + private List dataTypes; + + public Configuration dataTypes(DataType... types) { + this.dataTypes = Arrays.asList(types); + return this; + } + } + + private Property(String name, DataType type) { + this.name = name; + this.dataTypes = List.of(type); + } + + public Property(String name, Consumer options) { + var config = new Configuration(); + options.accept(config); + + this.name = name; + this.dataTypes = config.dataTypes; + } +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/dto/CollectionDefinitionDTO.java b/src/main/java/io/weaviate/client6/v1/collections/dto/CollectionDefinitionDTO.java new file mode 100644 index 000000000..a2f333b7a --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/dto/CollectionDefinitionDTO.java @@ -0,0 +1,35 @@ +package io.weaviate.client6.v1.collections.dto; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.gson.annotations.SerializedName; + +import io.weaviate.client6.v1.collections.CollectionDefinition; +import io.weaviate.client6.v1.collections.Property; + +public class CollectionDefinitionDTO { + @SerializedName("class") + String collection; + + @SerializedName("properties") + List properties; + + @SerializedName("vectorConfig") + Map vectorIndices; + + public CollectionDefinitionDTO(CollectionDefinition colDef) { + this.collection = colDef.name; + this.properties = colDef.properties; + + this.vectorIndices = new HashMap<>(); + for (var entry : colDef.vectorConfig.entrySet()) { + var index = entry.getValue(); + this.vectorIndices.put(entry.getKey(), Map.of( + "vectorizer", index.vectorizer(), + "vectorIndexType", index.indexType(), + "vectorIndexConfig", index.indexConfiguration())); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/data/CustomMetadata.java b/src/main/java/io/weaviate/client6/v1/data/CustomMetadata.java new file mode 100644 index 000000000..0c4c7bc81 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/CustomMetadata.java @@ -0,0 +1,28 @@ +package io.weaviate.client6.v1.data; + +import java.util.Map; +import java.util.function.Consumer; + +public class CustomMetadata { + String id; + Vectors vectors; + + public CustomMetadata id(String id) { + this.id = id; + return this; + } + + public CustomMetadata vectors(Vectors vectors) { + this.vectors = vectors; + return this; + } + + public CustomMetadata vectors(Map vectors) { + this.vectors = new Vectors(vectors); + return this; + } + + CustomMetadata(Consumer options) { + options.accept(this); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/data/Data.java b/src/main/java/io/weaviate/client6/v1/data/Data.java new file mode 100644 index 000000000..d058808ac --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/Data.java @@ -0,0 +1,102 @@ +package io.weaviate.client6.v1.data; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Optional; +import java.util.function.Consumer; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; + +import io.weaviate.client6.Config; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class Data { + private static final Gson gson = new Gson(); + + // TODO: this should be wrapped around in some TypeInspector etc. + private final String collectionName; + + // TODO: hide befind an internal HttpClient + private final Config config; + + public WeaviateObject insert(T object, Consumer metadata) throws IOException { + var body = new WeaviateObject<>(collectionName, object, metadata).toRequestObject(); + try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + ClassicHttpRequest httpPost = ClassicRequestBuilder + .post(config.baseUrl() + "/objects") + .setEntity(gson.toJson(body), ContentType.APPLICATION_JSON) + .build(); + + return httpclient.execute(httpPost, response -> { + var entity = response.getEntity(); + if (response.getCode() != HttpStatus.SC_SUCCESS) { // Does not return 201 + var message = EntityUtils.toString(entity); + throw new RuntimeException("HTTP " + response.getCode() + ": " + message); + } + + try (var r = new InputStreamReader(entity.getContent())) { + WeaviateObject.RequestObject inserted = gson.fromJson(r, + new TypeToken>() { + }.getType()); + return inserted.toWeaviateObject(); + } + }); + } + } + + public Optional> get(String id) throws IOException { + try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + ClassicHttpRequest httpGet = ClassicRequestBuilder + .get(config.baseUrl() + "/objects/" + collectionName + "/" + id + "?include=vector") + .build(); + + return httpclient.execute(httpGet, response -> { + if (response.getCode() == HttpStatus.SC_NOT_FOUND) { + return Optional.empty(); + } + var json = EntityUtils.toString(response.getEntity()); + WeaviateObject.RequestObject object = gson.fromJson(json, + new TypeToken>() { + }.getType()); + if (object == null) { + return Optional.empty(); + } + return Optional.of(object.toWeaviateObject()); + // try (var r = new InputStreamReader(response.getEntity().getContent())) { + // WeaviateObject.RequestObject object = gson.fromJson(r, + // new TypeToken>() { + // }.getType()); + // if (object == null) { + // return Optional.empty(); + // } + // return Optional.of(object.toWeaviateObject()); + // } + }); + } + } + + public void delete(String id) throws IOException { + try (CloseableHttpClient httpclient = HttpClients.createDefault()) { + ClassicHttpRequest httpGet = ClassicRequestBuilder + .delete(config.baseUrl() + "/objects/" + collectionName + "/" + id) + .build(); + + httpclient.execute(httpGet, response -> { + if (response.getCode() != HttpStatus.SC_NO_CONTENT) { + throw new RuntimeException(EntityUtils.toString(response.getEntity())); + } + return null; + }); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/data/Vectors.java b/src/main/java/io/weaviate/client6/v1/data/Vectors.java new file mode 100644 index 000000000..9a4fab29e --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/Vectors.java @@ -0,0 +1,86 @@ +package io.weaviate.client6.v1.data; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Vectors is an abstraction over named vectors. + * It may contain both 1-dimensional and 2-dimensional vectors. + */ +public class Vectors { + private static final String DEFAULT = "default"; + + private Map namedVectors; + + public static Vectors of(Float[] vector) { + return new Vectors(vector); + } + + public static Vectors of(Float[][] vector) { + return new Vectors(vector); + } + + public static Vectors of(String name, Float[] vector) { + return new Vectors(name, vector); + } + + public static Vectors of(String name, Float[][] vector) { + return new Vectors(name, vector); + } + + /** Creates immutable set of vectors. */ + Vectors(Map vectors) { + this.namedVectors = Collections.unmodifiableMap(vectors); + } + + /** Creates immutable set of vectors. */ + private Vectors(Float[] vector) { + this.namedVectors = Map.of(DEFAULT, vector); + } + + /** Creates immutable set of vectors. */ + private Vectors(Float[][] vector) { + this.namedVectors = Map.of(DEFAULT, vector); + } + + /** Creates extendable set of vectors. */ + private Vectors(String name, Object vector) { + this(); + this.namedVectors.put(name, vector); + } + + Vectors() { + this.namedVectors = new HashMap<>(); + } + + @SuppressWarnings("unchecked") + public Optional getSingle() { + return (Optional) getOnly(); + } + + public Optional getSingle(String name) { + return null; + } + + @SuppressWarnings("unchecked") + public Optional getMulti() { + return (Optional) getOnly(); + } + + public Optional getMulti(String name) { + return null; + } + + private Optional getOnly() { + if (namedVectors == null || namedVectors.isEmpty() || namedVectors.size() > 1) { + return Optional.empty(); + } + return Optional.ofNullable(namedVectors.values().iterator().next()); + } + + Map asMap() { + return Map.copyOf(namedVectors); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java new file mode 100644 index 000000000..ea71c214c --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java @@ -0,0 +1,63 @@ +package io.weaviate.client6.v1.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import com.google.gson.annotations.SerializedName; + +import lombok.AllArgsConstructor; + +// TODO: unify this with collections.SearchObject + +@AllArgsConstructor +public class WeaviateObject { + public final String collection; + public final T properties; + public final Metadata metadata; + + @AllArgsConstructor + public static class Metadata { + public final String id; + public final Vectors vectors; + } + + WeaviateObject(String collection, T properties, Consumer options) { + var metadata = new CustomMetadata(options); + + this.collection = collection; + this.properties = properties; + this.metadata = new Metadata(metadata.id, metadata.vectors); + } + + RequestObject toRequestObject() { + return new RequestObject(collection, metadata.id, properties, metadata.vectors.asMap()); + } + + @AllArgsConstructor + static class RequestObject { + @SerializedName("class") + public String collection; + @SerializedName("id") + public String id; + @SerializedName("properties") + public T properties; + @SerializedName("vectors") + public Map vectors; + + WeaviateObject toWeaviateObject() { + Map arrayVectors = new HashMap<>(); + for (var entry : vectors.entrySet()) { + var value = (ArrayList) entry.getValue(); + var vector = new Float[value.size()]; + int i = 0; + for (var v : value) { + vector[i++] = v.floatValue(); + } + arrayVectors.put(entry.getKey(), vector); + } + return new WeaviateObject(collection, properties, new Metadata(id, new Vectors(arrayVectors))); + } + } +} From 756f7f56d5fce73242eb0fd3ce793606104c4562 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sat, 8 Mar 2025 21:47:15 +0100 Subject: [PATCH 02/17] chore: commit missing classes --- .../v1/query/NearVectorQueryITest.java | 68 +++++++++++++++++++ .../weaviate/client6/v1/query/NearVector.java | 29 ++++++++ .../io/weaviate/client6/v1/query/Query.java | 11 +++ .../client6/v1/query/QueryOptions.java | 39 +++++++++++ .../client6/v1/query/SearchResult.java | 25 +++++++ 5 files changed, 172 insertions(+) create mode 100644 src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java create mode 100644 src/main/java/io/weaviate/client6/v1/query/NearVector.java create mode 100644 src/main/java/io/weaviate/client6/v1/query/Query.java create mode 100644 src/main/java/io/weaviate/client6/v1/query/QueryOptions.java create mode 100644 src/main/java/io/weaviate/client6/v1/query/SearchResult.java diff --git a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java new file mode 100644 index 000000000..f26388126 --- /dev/null +++ b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java @@ -0,0 +1,68 @@ +package io.weaviate.client6.v1.query; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.UUID; +import java.util.stream.IntStream; + +import org.assertj.core.api.Assertions; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.weaviate.client6.Config; +import io.weaviate.client6.WeaviateClient; +import io.weaviate.client6.v1.data.Vectors; +import io.weaviate.containers.Container; + +public class NearVectorQueryITest { + private static final WeaviateClient client = new WeaviateClient( + new Config("http", Container.WEAVIATE.getHttpHostAddress())); + + private static final String COLLECTION = "Things"; + private static final Random rand = new Random(); + + private static Float[] searchVector; + + @BeforeClass + public static void beforeAll() throws IOException { + var created = createVectors(10); + searchVector = created.values().iterator().next(); + } + + @Test + public void testNearVector() { + var things = client.collections.use(COLLECTION); + SearchResult> result = things.query.nearVector(searchVector, + opt -> opt + .distance(.002f) + .limit(3)); + + Assertions.assertThat(result.objects).hasSize(3); + float maxDistance = Collections.max(result.objects, + Comparator.comparing(obj -> obj.metadata.distance)).metadata.distance; + Assertions.assertThat(maxDistance).isLessThanOrEqualTo(.002f); + } + + static Map createVectors(int n) throws IOException { + var created = new HashMap(); + + var things = client.collections.use(COLLECTION); + for (int i = 0; i < n; i++) { + Float[] vector = IntStream.range(0, 9).mapToObj(f -> rand.nextFloat(-0.01f, 0.001f)) + .toArray(Float[]::new); + var object = things.data.insert( + Map.of(), + metadata -> metadata + .id(UUID.randomUUID().toString()) + .vectors(Vectors.of(vector))); + + created.put(object.metadata.id, vector); + } + + return created; + } +} diff --git a/src/main/java/io/weaviate/client6/v1/query/NearVector.java b/src/main/java/io/weaviate/client6/v1/query/NearVector.java new file mode 100644 index 000000000..edd0a26f0 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/query/NearVector.java @@ -0,0 +1,29 @@ +package io.weaviate.client6.v1.query; + +import java.util.function.Consumer; + +public class NearVector { + private final Float[] vector; + private final Options options; + + public static class Options extends QueryOptions { + private Float distance; + private Float certainty; + + public Options distance(float distance) { + this.distance = distance; + return this; + } + + public Options certainty(float certainty) { + this.certainty = certainty; + return this; + } + } + + public NearVector(Float[] vector, Consumer options) { + this.options = new Options(); + this.vector = vector; + options.accept(this.options); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/query/Query.java b/src/main/java/io/weaviate/client6/v1/query/Query.java new file mode 100644 index 000000000..c3b1f938b --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/query/Query.java @@ -0,0 +1,11 @@ +package io.weaviate.client6.v1.query; + +import java.util.function.Consumer; + +public class Query { + + public SearchResult nearVector(Float[] vector, Consumer options) { + var query = new NearVector(vector, options); + return null; + } +} diff --git a/src/main/java/io/weaviate/client6/v1/query/QueryOptions.java b/src/main/java/io/weaviate/client6/v1/query/QueryOptions.java new file mode 100644 index 000000000..2dae7d7f6 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/query/QueryOptions.java @@ -0,0 +1,39 @@ +package io.weaviate.client6.v1.query; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("unchecked") +abstract class QueryOptions> { + private Integer limit; + private Integer offset; + private Integer autocut; + private String after; + private String consistencyLevel; + private List returnProperties = new ArrayList<>(); + + public final SELF limit(Integer limit) { + this.limit = limit; + return (SELF) this; + } + + public final SELF offset(Integer offset) { + this.offset = offset; + return (SELF) this; + } + + public final SELF autocut(Integer autocut) { + this.autocut = autocut; + return (SELF) this; + } + + public final SELF after(String after) { + this.after = after; + return (SELF) this; + } + + public final SELF consistencyLevel(String consistencyLevel) { + this.consistencyLevel = consistencyLevel; + return (SELF) this; + } +} diff --git a/src/main/java/io/weaviate/client6/v1/query/SearchResult.java b/src/main/java/io/weaviate/client6/v1/query/SearchResult.java new file mode 100644 index 000000000..8a487435a --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/query/SearchResult.java @@ -0,0 +1,25 @@ +package io.weaviate.client6.v1.query; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.ToString; + +@AllArgsConstructor +public class SearchResult { + public final List> objects; + + @AllArgsConstructor + public static class SearchObject { + public final T properties; + public final SearchMetadata metadata; + + @AllArgsConstructor + @ToString + public static class SearchMetadata { + String id; + Float distance; + Float[] vector; + } + } +} From d2d4b33f4b8ec538d446dc60322531839734af8d Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sun, 9 Mar 2025 16:40:22 +0100 Subject: [PATCH 03/17] feat: implement simple NearVector search This is a first iteration, so I reused a lot of the 'experimental' code we wrote to 5.1.0-alpha1 and also did not care to much about code duplication and project structure. Will be refactored in the following iterations. Integration tests now have a set of utils, which allows them to re-use the same Weaviate instance across multiple test suites without collection name clashes. --- src/it/java/io/weaviate/ConcurrentTest.java | 64 ++ .../weaviate/client6/internal/GRPCTest.java | 48 + .../io/weaviate/client6/v1/DataITest.java | 19 +- .../v1/query/NearVectorQueryITest.java | 52 +- .../java/io/weaviate/containers/Weaviate.java | 2 +- src/main/java/io/weaviate/client6/Config.java | 18 +- .../grpc/protocol/v0/WeaviateGrpc.java | 367 -------- .../grpc/protocol/v0/WeaviateProto.java | 47 - .../grpc/protocol/v0/WeaviateProtoBatch.java | 855 ------------------ .../protocol/v0/WeaviateProtoSearchGet.java | 855 ------------------ .../io/weaviate/client6/internal/GRPC.java | 93 ++ .../weaviate/client6/internal/HttpClient.java | 4 - .../io/weaviate/client6/v1/Collection.java | 2 +- .../io/weaviate/client6/v1/data/Data.java | 1 + .../weaviate/client6/v1/query/Metadata.java | 12 + .../client6/v1/query/MetadataField.java | 28 + .../weaviate/client6/v1/query/NearVector.java | 35 +- .../io/weaviate/client6/v1/query/Query.java | 113 ++- .../client6/v1/query/QueryOptions.java | 45 + .../{SearchResult.java => QueryResult.java} | 7 +- src/main/proto/v0/batch.proto | 12 - src/main/proto/v0/search_get.proto | 12 - src/main/proto/v0/weaviate.proto | 15 - 23 files changed, 491 insertions(+), 2215 deletions(-) create mode 100644 src/it/java/io/weaviate/ConcurrentTest.java create mode 100644 src/it/java/io/weaviate/client6/internal/GRPCTest.java delete mode 100644 src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateGrpc.java delete mode 100644 src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProto.java delete mode 100644 src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProtoBatch.java delete mode 100644 src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProtoSearchGet.java create mode 100644 src/main/java/io/weaviate/client6/internal/GRPC.java delete mode 100644 src/main/java/io/weaviate/client6/internal/HttpClient.java create mode 100644 src/main/java/io/weaviate/client6/v1/query/Metadata.java create mode 100644 src/main/java/io/weaviate/client6/v1/query/MetadataField.java rename src/main/java/io/weaviate/client6/v1/query/{SearchResult.java => QueryResult.java} (69%) delete mode 100644 src/main/proto/v0/batch.proto delete mode 100644 src/main/proto/v0/search_get.proto delete mode 100644 src/main/proto/v0/weaviate.proto diff --git a/src/it/java/io/weaviate/ConcurrentTest.java b/src/it/java/io/weaviate/ConcurrentTest.java new file mode 100644 index 000000000..570cffa36 --- /dev/null +++ b/src/it/java/io/weaviate/ConcurrentTest.java @@ -0,0 +1,64 @@ +package io.weaviate; + +import java.util.Random; +import java.util.UUID; +import java.util.stream.IntStream; + +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.Rule; +import org.junit.rules.TestName; + +/** + * ConcurrentTest is the base class for integration tests, which provides + * utility methods to uniqualize collections and objects created in the + * database. + * + * Because we want to re-use the same database container across most of the + * test suites and (eventually) run them in parallel, + * test classes should extend this class and use its methods + * to avoid name clashes in the shared Weaviate instance. + */ +public abstract class ConcurrentTest { + @Rule + public TestName currentTest = new TestName(); + + protected static final Random rand = new Random(); + + /** + * Add unique namespace prefix to the string. + * + * @param value Collection name, object ID, etc., which has to be unique across + * all test suites. + * @return Value prefixed with the name of the current test suite + test method. + */ + protected String ns(String value) { + String cls = getClass().getSimpleName(); + String method = currentTest.getMethodName(); + return cls + "." + method + "-" + value; + } + + /** Appends random characters to create unique value. */ + protected static String unique(String value) { + var randString = RandomStringUtils.insecure().next(8, true, false); + return value + "_" + randString; + } + + /** Generate random UUID. */ + protected static String randomUUID() { + return UUID.randomUUID().toString(); + } + + /** + * Generate a random vector. + * + * @param length Vector length. + * @param origin Value range lower bound. + * @param bound Value range upper bound. + * @return + */ + protected static Float[] randomVector(int length, float origin, float bound) { + return IntStream.range(0, length) + .mapToObj(f -> rand.nextFloat(origin, bound)) + .toArray(Float[]::new); + } +} diff --git a/src/it/java/io/weaviate/client6/internal/GRPCTest.java b/src/it/java/io/weaviate/client6/internal/GRPCTest.java new file mode 100644 index 000000000..ab14b6aaa --- /dev/null +++ b/src/it/java/io/weaviate/client6/internal/GRPCTest.java @@ -0,0 +1,48 @@ +package io.weaviate.client6.internal; + +import static org.junit.Assert.assertArrayEquals; + +import org.junit.Test; + +import com.google.protobuf.ByteString; + +/** + * Note: Java's {@code byte} is signed (int8) and is different from {@code byte} + * in Go, which is an alias for uint8. + * + * For this tests purposes the distinction is immaterial, as "want" arrays + * are "golden values" meant to be a readable respresentation for the test. + */ +public class GRPCTest { + @Test + public void test_toBytesString_1d() { + Float[] vector = { 1f, 2f, 3f }; + byte[] want = { 0, 0, -128, 63, 0, 0, 0, 64, 0, 0, 64, 64 }; + byte[] got = GRPC.toByteString(vector).toByteArray(); + assertArrayEquals(want, got); + } + + @Test + public void test_fromBytesString_1d() { + byte[] bytes = { 0, 0, -128, 63, 0, 0, 0, 64, 0, 0, 64, 64 }; + Float[] want = { 1f, 2f, 3f }; + Float[] got = GRPC.fromByteString(ByteString.copyFrom(bytes)); + assertArrayEquals(want, got); + } + + @Test + public void test_toBytesString_2d() { + Float[][] vector = { { 1f, 2f, 3f }, { 4f, 5f, 6f } }; + byte[] want = { 3, 0, 0, 0, -128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, -128, 64, 0, 0, -96, 64, 0, 0, -64, 64 }; + byte[] got = GRPC.toByteString(vector).toByteArray(); + assertArrayEquals(want, got); + } + + @Test + public void test_fromBytesString_2d() { + byte[] bytes = { 3, 0, 0, 0, -128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, -128, 64, 0, 0, -96, 64, 0, 0, -64, 64 }; + Float[][] want = { { 1f, 2f, 3f }, { 4f, 5f, 6f } }; + Float[][] got = GRPC.fromByteStringMulti(ByteString.copyFrom(bytes)); + assertArrayEquals(want, got); + } +} diff --git a/src/it/java/io/weaviate/client6/v1/DataITest.java b/src/it/java/io/weaviate/client6/v1/DataITest.java index b056598e7..865954290 100644 --- a/src/it/java/io/weaviate/client6/v1/DataITest.java +++ b/src/it/java/io/weaviate/client6/v1/DataITest.java @@ -2,43 +2,32 @@ import java.io.IOException; import java.util.Map; -import java.util.UUID; import org.assertj.core.api.Assertions; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.BeforeClass; import org.junit.Test; +import io.weaviate.ConcurrentTest; import io.weaviate.client6.WeaviateClient; import io.weaviate.client6.v1.collections.Property; import io.weaviate.client6.v1.data.Vectors; import io.weaviate.containers.Container; -public class DataITest { - // public static final Container.Group compose = Container.compose( - // Weaviate.custom() - // .withDefaultVectorizer(Contextionary.MODULE) - // .withContextionaryUrl(Contextionary.URL) - // .build(), - // Container.CONTEXTIONARY); +public class DataITest extends ConcurrentTest { private static WeaviateClient client = Container.WEAVIATE.getClient(); - - // @ClassRule - // public static TestRule _rule = compose.asTestRule(); - - private static final String COLLECTION = "Things"; + private static final String COLLECTION = unique("Things"); @BeforeClass public static void beforeAll() throws IOException { - // client = compose.getClient(); createTestCollection(); } @Test public void testCreateGetDelete() throws IOException { var things = client.collections.use(COLLECTION); - var id = UUID.randomUUID().toString(); + var id = randomUUID(); Float[] vector = { 1f, 2f, 3f }; things.data.insert(Map.of("username", "john doe"), metadata -> metadata diff --git a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java index f26388126..dbefa2bdd 100644 --- a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java +++ b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java @@ -5,64 +5,80 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Map; -import java.util.Random; -import java.util.UUID; -import java.util.stream.IntStream; import org.assertj.core.api.Assertions; import org.junit.BeforeClass; import org.junit.Test; -import io.weaviate.client6.Config; +import io.weaviate.ConcurrentTest; import io.weaviate.client6.WeaviateClient; import io.weaviate.client6.v1.data.Vectors; import io.weaviate.containers.Container; -public class NearVectorQueryITest { - private static final WeaviateClient client = new WeaviateClient( - new Config("http", Container.WEAVIATE.getHttpHostAddress())); +public class NearVectorQueryITest extends ConcurrentTest { + private static final WeaviateClient client = Container.WEAVIATE.getClient(); - private static final String COLLECTION = "Things"; - private static final Random rand = new Random(); + private static final String COLLECTION = unique("Things"); + private static final String VECTOR_INDEX = "bring_your_own"; + /** + * One of the inserted vectors which will be used as target vector for search. + */ private static Float[] searchVector; @BeforeClass public static void beforeAll() throws IOException { + createTestCollection(); var created = createVectors(10); searchVector = created.values().iterator().next(); } @Test public void testNearVector() { + // TODO: test that we return the results in the expected order + // Because re-ranking should work correctly var things = client.collections.use(COLLECTION); - SearchResult> result = things.query.nearVector(searchVector, + QueryResult> result = things.query.nearVector(searchVector, opt -> opt - .distance(.002f) - .limit(3)); + .distance(2f) + .limit(3) + .returnMetadata(MetadataField.DISTANCE)); Assertions.assertThat(result.objects).hasSize(3); float maxDistance = Collections.max(result.objects, Comparator.comparing(obj -> obj.metadata.distance)).metadata.distance; - Assertions.assertThat(maxDistance).isLessThanOrEqualTo(.002f); + Assertions.assertThat(maxDistance).isLessThanOrEqualTo(2f); } - static Map createVectors(int n) throws IOException { + /** + * Insert 10 objects with random vectors. + * + * @returns IDs of inserted objects and their corresponding vectors. + */ + private static Map createVectors(int n) throws IOException { var created = new HashMap(); var things = client.collections.use(COLLECTION); for (int i = 0; i < n; i++) { - Float[] vector = IntStream.range(0, 9).mapToObj(f -> rand.nextFloat(-0.01f, 0.001f)) - .toArray(Float[]::new); + var vector = randomVector(10, -.01f, .001f); var object = things.data.insert( Map.of(), metadata -> metadata - .id(UUID.randomUUID().toString()) - .vectors(Vectors.of(vector))); + .id(randomUUID()) + .vectors(Vectors.of(VECTOR_INDEX, vector))); created.put(object.metadata.id, vector); } return created; } + + /** + * Create {@link COLLECTION} with {@link VECTOR_INDEX} vector index. + * + * @throws IOException + */ + private static void createTestCollection() throws IOException { + client.collections.create(COLLECTION, col -> col.vector(VECTOR_INDEX)); + } } diff --git a/src/it/java/io/weaviate/containers/Weaviate.java b/src/it/java/io/weaviate/containers/Weaviate.java index 1c587e305..71cf83de3 100644 --- a/src/it/java/io/weaviate/containers/Weaviate.java +++ b/src/it/java/io/weaviate/containers/Weaviate.java @@ -13,7 +13,7 @@ public class Weaviate extends WeaviateContainer { public static final String DOCKER_IMAGE = "semitechnologies/weaviate"; public WeaviateClient getClient() { - var config = new Config("http", getHttpHostAddress()); + var config = new Config("http", getHttpHostAddress(), getGrpcHostAddress()); return new WeaviateClient(config); } diff --git a/src/main/java/io/weaviate/client6/Config.java b/src/main/java/io/weaviate/client6/Config.java index dafe90077..8926c3aa8 100644 --- a/src/main/java/io/weaviate/client6/Config.java +++ b/src/main/java/io/weaviate/client6/Config.java @@ -3,14 +3,24 @@ public class Config { private final String version = "v1"; private final String scheme; - private final String host; + private final String httpHost; + private final String grpcHost; - public Config(String scheme, String host) { + public Config(String scheme, String httpHost, String grpcHost) { this.scheme = scheme; - this.host = host; + this.httpHost = httpHost; + this.grpcHost = grpcHost; } public String baseUrl() { - return scheme + "://" + host + "/" + version; + return scheme + "://" + httpHost + "/" + version; + } + + public String grpcAddress() { + if (grpcHost.contains(":")) { + return grpcHost; + } + // FIXME: use secure port (433) if scheme == https + return String.format("%s:80", grpcHost); } } diff --git a/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateGrpc.java b/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateGrpc.java deleted file mode 100644 index ed743de27..000000000 --- a/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateGrpc.java +++ /dev/null @@ -1,367 +0,0 @@ -package io.weaviate.client6.grpc.protocol.v0; - -import static io.grpc.MethodDescriptor.generateFullMethodName; - -/** - */ -@javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.58.0)", - comments = "Source: v0/weaviate.proto") -@io.grpc.stub.annotations.GrpcGenerated -public final class WeaviateGrpc { - - private WeaviateGrpc() {} - - public static final java.lang.String SERVICE_NAME = "weaviategrpc.Weaviate"; - - // Static method descriptors that strictly reflect the proto. - private static volatile io.grpc.MethodDescriptor getSearchMethod; - - @io.grpc.stub.annotations.RpcMethod( - fullMethodName = SERVICE_NAME + '/' + "Search", - requestType = io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.class, - responseType = io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.class, - methodType = io.grpc.MethodDescriptor.MethodType.UNARY) - public static io.grpc.MethodDescriptor getSearchMethod() { - io.grpc.MethodDescriptor getSearchMethod; - if ((getSearchMethod = WeaviateGrpc.getSearchMethod) == null) { - synchronized (WeaviateGrpc.class) { - if ((getSearchMethod = WeaviateGrpc.getSearchMethod) == null) { - WeaviateGrpc.getSearchMethod = getSearchMethod = - io.grpc.MethodDescriptor.newBuilder() - .setType(io.grpc.MethodDescriptor.MethodType.UNARY) - .setFullMethodName(generateFullMethodName(SERVICE_NAME, "Search")) - .setSampledToLocalTracing(true) - .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.getDefaultInstance())) - .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.getDefaultInstance())) - .setSchemaDescriptor(new WeaviateMethodDescriptorSupplier("Search")) - .build(); - } - } - } - return getSearchMethod; - } - - private static volatile io.grpc.MethodDescriptor getBatchObjectsMethod; - - @io.grpc.stub.annotations.RpcMethod( - fullMethodName = SERVICE_NAME + '/' + "BatchObjects", - requestType = io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.class, - responseType = io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.class, - methodType = io.grpc.MethodDescriptor.MethodType.UNARY) - public static io.grpc.MethodDescriptor getBatchObjectsMethod() { - io.grpc.MethodDescriptor getBatchObjectsMethod; - if ((getBatchObjectsMethod = WeaviateGrpc.getBatchObjectsMethod) == null) { - synchronized (WeaviateGrpc.class) { - if ((getBatchObjectsMethod = WeaviateGrpc.getBatchObjectsMethod) == null) { - WeaviateGrpc.getBatchObjectsMethod = getBatchObjectsMethod = - io.grpc.MethodDescriptor.newBuilder() - .setType(io.grpc.MethodDescriptor.MethodType.UNARY) - .setFullMethodName(generateFullMethodName(SERVICE_NAME, "BatchObjects")) - .setSampledToLocalTracing(true) - .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.getDefaultInstance())) - .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.getDefaultInstance())) - .setSchemaDescriptor(new WeaviateMethodDescriptorSupplier("BatchObjects")) - .build(); - } - } - } - return getBatchObjectsMethod; - } - - /** - * Creates a new async stub that supports all call types for the service - */ - public static WeaviateStub newStub(io.grpc.Channel channel) { - io.grpc.stub.AbstractStub.StubFactory factory = - new io.grpc.stub.AbstractStub.StubFactory() { - @java.lang.Override - public WeaviateStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - return new WeaviateStub(channel, callOptions); - } - }; - return WeaviateStub.newStub(factory, channel); - } - - /** - * Creates a new blocking-style stub that supports unary and streaming output calls on the service - */ - public static WeaviateBlockingStub newBlockingStub( - io.grpc.Channel channel) { - io.grpc.stub.AbstractStub.StubFactory factory = - new io.grpc.stub.AbstractStub.StubFactory() { - @java.lang.Override - public WeaviateBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - return new WeaviateBlockingStub(channel, callOptions); - } - }; - return WeaviateBlockingStub.newStub(factory, channel); - } - - /** - * Creates a new ListenableFuture-style stub that supports unary calls on the service - */ - public static WeaviateFutureStub newFutureStub( - io.grpc.Channel channel) { - io.grpc.stub.AbstractStub.StubFactory factory = - new io.grpc.stub.AbstractStub.StubFactory() { - @java.lang.Override - public WeaviateFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - return new WeaviateFutureStub(channel, callOptions); - } - }; - return WeaviateFutureStub.newStub(factory, channel); - } - - /** - */ - public interface AsyncService { - - /** - */ - default void search(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest request, - io.grpc.stub.StreamObserver responseObserver) { - io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSearchMethod(), responseObserver); - } - - /** - */ - default void batchObjects(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest request, - io.grpc.stub.StreamObserver responseObserver) { - io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getBatchObjectsMethod(), responseObserver); - } - } - - /** - * Base class for the server implementation of the service Weaviate. - */ - public static abstract class WeaviateImplBase - implements io.grpc.BindableService, AsyncService { - - @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() { - return WeaviateGrpc.bindService(this); - } - } - - /** - * A stub to allow clients to do asynchronous rpc calls to service Weaviate. - */ - public static final class WeaviateStub - extends io.grpc.stub.AbstractAsyncStub { - private WeaviateStub( - io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - super(channel, callOptions); - } - - @java.lang.Override - protected WeaviateStub build( - io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - return new WeaviateStub(channel, callOptions); - } - - /** - */ - public void search(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest request, - io.grpc.stub.StreamObserver responseObserver) { - io.grpc.stub.ClientCalls.asyncUnaryCall( - getChannel().newCall(getSearchMethod(), getCallOptions()), request, responseObserver); - } - - /** - */ - public void batchObjects(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest request, - io.grpc.stub.StreamObserver responseObserver) { - io.grpc.stub.ClientCalls.asyncUnaryCall( - getChannel().newCall(getBatchObjectsMethod(), getCallOptions()), request, responseObserver); - } - } - - /** - * A stub to allow clients to do synchronous rpc calls to service Weaviate. - */ - public static final class WeaviateBlockingStub - extends io.grpc.stub.AbstractBlockingStub { - private WeaviateBlockingStub( - io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - super(channel, callOptions); - } - - @java.lang.Override - protected WeaviateBlockingStub build( - io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - return new WeaviateBlockingStub(channel, callOptions); - } - - /** - */ - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply search(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( - getChannel(), getSearchMethod(), getCallOptions(), request); - } - - /** - */ - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply batchObjects(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( - getChannel(), getBatchObjectsMethod(), getCallOptions(), request); - } - } - - /** - * A stub to allow clients to do ListenableFuture-style rpc calls to service Weaviate. - */ - public static final class WeaviateFutureStub - extends io.grpc.stub.AbstractFutureStub { - private WeaviateFutureStub( - io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - super(channel, callOptions); - } - - @java.lang.Override - protected WeaviateFutureStub build( - io.grpc.Channel channel, io.grpc.CallOptions callOptions) { - return new WeaviateFutureStub(channel, callOptions); - } - - /** - */ - public com.google.common.util.concurrent.ListenableFuture search( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest request) { - return io.grpc.stub.ClientCalls.futureUnaryCall( - getChannel().newCall(getSearchMethod(), getCallOptions()), request); - } - - /** - */ - public com.google.common.util.concurrent.ListenableFuture batchObjects( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest request) { - return io.grpc.stub.ClientCalls.futureUnaryCall( - getChannel().newCall(getBatchObjectsMethod(), getCallOptions()), request); - } - } - - private static final int METHODID_SEARCH = 0; - private static final int METHODID_BATCH_OBJECTS = 1; - - private static final class MethodHandlers implements - io.grpc.stub.ServerCalls.UnaryMethod, - io.grpc.stub.ServerCalls.ServerStreamingMethod, - io.grpc.stub.ServerCalls.ClientStreamingMethod, - io.grpc.stub.ServerCalls.BidiStreamingMethod { - private final AsyncService serviceImpl; - private final int methodId; - - MethodHandlers(AsyncService serviceImpl, int methodId) { - this.serviceImpl = serviceImpl; - this.methodId = methodId; - } - - @java.lang.Override - @java.lang.SuppressWarnings("unchecked") - public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { - switch (methodId) { - case METHODID_SEARCH: - serviceImpl.search((io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest) request, - (io.grpc.stub.StreamObserver) responseObserver); - break; - case METHODID_BATCH_OBJECTS: - serviceImpl.batchObjects((io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest) request, - (io.grpc.stub.StreamObserver) responseObserver); - break; - default: - throw new AssertionError(); - } - } - - @java.lang.Override - @java.lang.SuppressWarnings("unchecked") - public io.grpc.stub.StreamObserver invoke( - io.grpc.stub.StreamObserver responseObserver) { - switch (methodId) { - default: - throw new AssertionError(); - } - } - } - - public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) { - return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) - .addMethod( - getSearchMethod(), - io.grpc.stub.ServerCalls.asyncUnaryCall( - new MethodHandlers< - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest, - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply>( - service, METHODID_SEARCH))) - .addMethod( - getBatchObjectsMethod(), - io.grpc.stub.ServerCalls.asyncUnaryCall( - new MethodHandlers< - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest, - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply>( - service, METHODID_BATCH_OBJECTS))) - .build(); - } - - private static abstract class WeaviateBaseDescriptorSupplier - implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { - WeaviateBaseDescriptorSupplier() {} - - @java.lang.Override - public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProto.getDescriptor(); - } - - @java.lang.Override - public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { - return getFileDescriptor().findServiceByName("Weaviate"); - } - } - - private static final class WeaviateFileDescriptorSupplier - extends WeaviateBaseDescriptorSupplier { - WeaviateFileDescriptorSupplier() {} - } - - private static final class WeaviateMethodDescriptorSupplier - extends WeaviateBaseDescriptorSupplier - implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { - private final java.lang.String methodName; - - WeaviateMethodDescriptorSupplier(java.lang.String methodName) { - this.methodName = methodName; - } - - @java.lang.Override - public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { - return getServiceDescriptor().findMethodByName(methodName); - } - } - - private static volatile io.grpc.ServiceDescriptor serviceDescriptor; - - public static io.grpc.ServiceDescriptor getServiceDescriptor() { - io.grpc.ServiceDescriptor result = serviceDescriptor; - if (result == null) { - synchronized (WeaviateGrpc.class) { - result = serviceDescriptor; - if (result == null) { - serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) - .setSchemaDescriptor(new WeaviateFileDescriptorSupplier()) - .addMethod(getSearchMethod()) - .addMethod(getBatchObjectsMethod()) - .build(); - } - } - } - return result; - } -} diff --git a/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProto.java b/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProto.java deleted file mode 100644 index 84447a78d..000000000 --- a/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProto.java +++ /dev/null @@ -1,47 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: v0/weaviate.proto - -package io.weaviate.client6.grpc.protocol.v0; - -public final class WeaviateProto { - private WeaviateProto() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistryLite registry) { - } - - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - registerAllExtensions( - (com.google.protobuf.ExtensionRegistryLite) registry); - } - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\021v0/weaviate.proto\022\014weaviategrpc\032\016v0/ba" + - "tch.proto\032\023v0/search_get.proto2\244\001\n\010Weavi" + - "ate\022B\n\006Search\022\033.weaviategrpc.SearchReque" + - "st\032\031.weaviategrpc.SearchReply\"\000\022T\n\014Batch" + - "Objects\022!.weaviategrpc.BatchObjectsReque" + - "st\032\037.weaviategrpc.BatchObjectsReply\"\000Bk\n" + - "$io.weaviate.client6.grpc.protocol.v0B\rW" + - "eaviateProtoZ4github.com/weaviate/weavia" + - "te/grpc/generated;protocolb\006proto3" - }; - descriptor = com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.getDescriptor(), - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.getDescriptor(), - }); - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.getDescriptor(); - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.getDescriptor(); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProtoBatch.java b/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProtoBatch.java deleted file mode 100644 index dd4254e7f..000000000 --- a/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProtoBatch.java +++ /dev/null @@ -1,855 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: v0/batch.proto - -package io.weaviate.client6.grpc.protocol.v0; - -public final class WeaviateProtoBatch { - private WeaviateProtoBatch() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistryLite registry) { - } - - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - registerAllExtensions( - (com.google.protobuf.ExtensionRegistryLite) registry); - } - public interface BatchObjectsRequestOrBuilder extends - // @@protoc_insertion_point(interface_extends:weaviategrpc.BatchObjectsRequest) - com.google.protobuf.MessageOrBuilder { - } - /** - * Protobuf type {@code weaviategrpc.BatchObjectsRequest} - */ - public static final class BatchObjectsRequest extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:weaviategrpc.BatchObjectsRequest) - BatchObjectsRequestOrBuilder { - private static final long serialVersionUID = 0L; - // Use BatchObjectsRequest.newBuilder() to construct. - private BatchObjectsRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - private BatchObjectsRequest() { - } - - @java.lang.Override - @SuppressWarnings({"unused"}) - protected java.lang.Object newInstance( - UnusedPrivateParameter unused) { - return new BatchObjectsRequest(); - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsRequest_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsRequest_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.class, io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.Builder.class); - } - - private byte memoizedIsInitialized = -1; - @java.lang.Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - memoizedIsInitialized = 1; - return true; - } - - @java.lang.Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getUnknownFields().writeTo(output); - } - - @java.lang.Override - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - size += getUnknownFields().getSerializedSize(); - memoizedSize = size; - return size; - } - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest)) { - return super.equals(obj); - } - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest other = (io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest) obj; - - if (!getUnknownFields().equals(other.getUnknownFields())) return false; - return true; - } - - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (29 * hash) + getUnknownFields().hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - @java.lang.Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - @java.lang.Override - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code weaviategrpc.BatchObjectsRequest} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:weaviategrpc.BatchObjectsRequest) - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequestOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsRequest_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsRequest_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.class, io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.Builder.class); - } - - // Construct using io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.newBuilder() - private Builder() { - - } - - private Builder( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - super(parent); - - } - @java.lang.Override - public Builder clear() { - super.clear(); - return this; - } - - @java.lang.Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsRequest_descriptor; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest getDefaultInstanceForType() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.getDefaultInstance(); - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest build() { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest buildPartial() { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest result = new io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest(this); - onBuilt(); - return result; - } - - @java.lang.Override - public Builder clone() { - return super.clone(); - } - @java.lang.Override - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { - return super.setField(field, value); - } - @java.lang.Override - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return super.clearField(field); - } - @java.lang.Override - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return super.clearOneof(oneof); - } - @java.lang.Override - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, java.lang.Object value) { - return super.setRepeatedField(field, index, value); - } - @java.lang.Override - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { - return super.addRepeatedField(field, value); - } - @java.lang.Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest) { - return mergeFrom((io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest other) { - if (other == io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest.getDefaultInstance()) return this; - this.mergeUnknownFields(other.getUnknownFields()); - onChanged(); - return this; - } - - @java.lang.Override - public final boolean isInitialized() { - return true; - } - - @java.lang.Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - if (extensionRegistry == null) { - throw new java.lang.NullPointerException(); - } - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!super.parseUnknownField(input, extensionRegistry, tag)) { - done = true; // was an endgroup tag - } - break; - } // default: - } // switch (tag) - } // while (!done) - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } finally { - onChanged(); - } // finally - return this; - } - @java.lang.Override - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.setUnknownFields(unknownFields); - } - - @java.lang.Override - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.mergeUnknownFields(unknownFields); - } - - - // @@protoc_insertion_point(builder_scope:weaviategrpc.BatchObjectsRequest) - } - - // @@protoc_insertion_point(class_scope:weaviategrpc.BatchObjectsRequest) - private static final io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest(); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - @java.lang.Override - public BatchObjectsRequest parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (com.google.protobuf.UninitializedMessageException e) { - throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException(e) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsRequest getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - - } - - public interface BatchObjectsReplyOrBuilder extends - // @@protoc_insertion_point(interface_extends:weaviategrpc.BatchObjectsReply) - com.google.protobuf.MessageOrBuilder { - } - /** - * Protobuf type {@code weaviategrpc.BatchObjectsReply} - */ - public static final class BatchObjectsReply extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:weaviategrpc.BatchObjectsReply) - BatchObjectsReplyOrBuilder { - private static final long serialVersionUID = 0L; - // Use BatchObjectsReply.newBuilder() to construct. - private BatchObjectsReply(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - private BatchObjectsReply() { - } - - @java.lang.Override - @SuppressWarnings({"unused"}) - protected java.lang.Object newInstance( - UnusedPrivateParameter unused) { - return new BatchObjectsReply(); - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsReply_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsReply_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.class, io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.Builder.class); - } - - private byte memoizedIsInitialized = -1; - @java.lang.Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - memoizedIsInitialized = 1; - return true; - } - - @java.lang.Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getUnknownFields().writeTo(output); - } - - @java.lang.Override - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - size += getUnknownFields().getSerializedSize(); - memoizedSize = size; - return size; - } - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply)) { - return super.equals(obj); - } - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply other = (io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply) obj; - - if (!getUnknownFields().equals(other.getUnknownFields())) return false; - return true; - } - - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (29 * hash) + getUnknownFields().hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - @java.lang.Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - @java.lang.Override - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code weaviategrpc.BatchObjectsReply} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:weaviategrpc.BatchObjectsReply) - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReplyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsReply_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsReply_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.class, io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.Builder.class); - } - - // Construct using io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.newBuilder() - private Builder() { - - } - - private Builder( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - super(parent); - - } - @java.lang.Override - public Builder clear() { - super.clear(); - return this; - } - - @java.lang.Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.internal_static_weaviategrpc_BatchObjectsReply_descriptor; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply getDefaultInstanceForType() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.getDefaultInstance(); - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply build() { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply buildPartial() { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply result = new io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply(this); - onBuilt(); - return result; - } - - @java.lang.Override - public Builder clone() { - return super.clone(); - } - @java.lang.Override - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { - return super.setField(field, value); - } - @java.lang.Override - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return super.clearField(field); - } - @java.lang.Override - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return super.clearOneof(oneof); - } - @java.lang.Override - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, java.lang.Object value) { - return super.setRepeatedField(field, index, value); - } - @java.lang.Override - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { - return super.addRepeatedField(field, value); - } - @java.lang.Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply) { - return mergeFrom((io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply other) { - if (other == io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply.getDefaultInstance()) return this; - this.mergeUnknownFields(other.getUnknownFields()); - onChanged(); - return this; - } - - @java.lang.Override - public final boolean isInitialized() { - return true; - } - - @java.lang.Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - if (extensionRegistry == null) { - throw new java.lang.NullPointerException(); - } - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!super.parseUnknownField(input, extensionRegistry, tag)) { - done = true; // was an endgroup tag - } - break; - } // default: - } // switch (tag) - } // while (!done) - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } finally { - onChanged(); - } // finally - return this; - } - @java.lang.Override - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.setUnknownFields(unknownFields); - } - - @java.lang.Override - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.mergeUnknownFields(unknownFields); - } - - - // @@protoc_insertion_point(builder_scope:weaviategrpc.BatchObjectsReply) - } - - // @@protoc_insertion_point(class_scope:weaviategrpc.BatchObjectsReply) - private static final io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply(); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - @java.lang.Override - public BatchObjectsReply parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (com.google.protobuf.UninitializedMessageException e) { - throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException(e) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoBatch.BatchObjectsReply getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - - } - - private static final com.google.protobuf.Descriptors.Descriptor - internal_static_weaviategrpc_BatchObjectsRequest_descriptor; - private static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_weaviategrpc_BatchObjectsRequest_fieldAccessorTable; - private static final com.google.protobuf.Descriptors.Descriptor - internal_static_weaviategrpc_BatchObjectsReply_descriptor; - private static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_weaviategrpc_BatchObjectsReply_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\016v0/batch.proto\022\014weaviategrpc\"\025\n\023BatchO" + - "bjectsRequest\"\023\n\021BatchObjectsReplyBp\n$io" + - ".weaviate.client6.grpc.protocol.v0B\022Weav" + - "iateProtoBatchZ4github.com/weaviate/weav" + - "iate/grpc/generated;protocolb\006proto3" - }; - descriptor = com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }); - internal_static_weaviategrpc_BatchObjectsRequest_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_weaviategrpc_BatchObjectsRequest_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_weaviategrpc_BatchObjectsRequest_descriptor, - new java.lang.String[] { }); - internal_static_weaviategrpc_BatchObjectsReply_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_weaviategrpc_BatchObjectsReply_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_weaviategrpc_BatchObjectsReply_descriptor, - new java.lang.String[] { }); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProtoSearchGet.java b/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProtoSearchGet.java deleted file mode 100644 index 5a67dccfc..000000000 --- a/src/main/java/io/weaviate/client6/grpc/protocol/v0/WeaviateProtoSearchGet.java +++ /dev/null @@ -1,855 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: v0/search_get.proto - -package io.weaviate.client6.grpc.protocol.v0; - -public final class WeaviateProtoSearchGet { - private WeaviateProtoSearchGet() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistryLite registry) { - } - - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - registerAllExtensions( - (com.google.protobuf.ExtensionRegistryLite) registry); - } - public interface SearchRequestOrBuilder extends - // @@protoc_insertion_point(interface_extends:weaviategrpc.SearchRequest) - com.google.protobuf.MessageOrBuilder { - } - /** - * Protobuf type {@code weaviategrpc.SearchRequest} - */ - public static final class SearchRequest extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:weaviategrpc.SearchRequest) - SearchRequestOrBuilder { - private static final long serialVersionUID = 0L; - // Use SearchRequest.newBuilder() to construct. - private SearchRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - private SearchRequest() { - } - - @java.lang.Override - @SuppressWarnings({"unused"}) - protected java.lang.Object newInstance( - UnusedPrivateParameter unused) { - return new SearchRequest(); - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchRequest_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchRequest_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.class, io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.Builder.class); - } - - private byte memoizedIsInitialized = -1; - @java.lang.Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - memoizedIsInitialized = 1; - return true; - } - - @java.lang.Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getUnknownFields().writeTo(output); - } - - @java.lang.Override - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - size += getUnknownFields().getSerializedSize(); - memoizedSize = size; - return size; - } - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest)) { - return super.equals(obj); - } - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest other = (io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest) obj; - - if (!getUnknownFields().equals(other.getUnknownFields())) return false; - return true; - } - - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (29 * hash) + getUnknownFields().hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - @java.lang.Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - @java.lang.Override - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code weaviategrpc.SearchRequest} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:weaviategrpc.SearchRequest) - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequestOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchRequest_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchRequest_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.class, io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.Builder.class); - } - - // Construct using io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.newBuilder() - private Builder() { - - } - - private Builder( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - super(parent); - - } - @java.lang.Override - public Builder clear() { - super.clear(); - return this; - } - - @java.lang.Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchRequest_descriptor; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest getDefaultInstanceForType() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.getDefaultInstance(); - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest build() { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest buildPartial() { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest result = new io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest(this); - onBuilt(); - return result; - } - - @java.lang.Override - public Builder clone() { - return super.clone(); - } - @java.lang.Override - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { - return super.setField(field, value); - } - @java.lang.Override - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return super.clearField(field); - } - @java.lang.Override - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return super.clearOneof(oneof); - } - @java.lang.Override - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, java.lang.Object value) { - return super.setRepeatedField(field, index, value); - } - @java.lang.Override - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { - return super.addRepeatedField(field, value); - } - @java.lang.Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest) { - return mergeFrom((io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest other) { - if (other == io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest.getDefaultInstance()) return this; - this.mergeUnknownFields(other.getUnknownFields()); - onChanged(); - return this; - } - - @java.lang.Override - public final boolean isInitialized() { - return true; - } - - @java.lang.Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - if (extensionRegistry == null) { - throw new java.lang.NullPointerException(); - } - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!super.parseUnknownField(input, extensionRegistry, tag)) { - done = true; // was an endgroup tag - } - break; - } // default: - } // switch (tag) - } // while (!done) - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } finally { - onChanged(); - } // finally - return this; - } - @java.lang.Override - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.setUnknownFields(unknownFields); - } - - @java.lang.Override - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.mergeUnknownFields(unknownFields); - } - - - // @@protoc_insertion_point(builder_scope:weaviategrpc.SearchRequest) - } - - // @@protoc_insertion_point(class_scope:weaviategrpc.SearchRequest) - private static final io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest(); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - @java.lang.Override - public SearchRequest parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (com.google.protobuf.UninitializedMessageException e) { - throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException(e) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchRequest getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - - } - - public interface SearchReplyOrBuilder extends - // @@protoc_insertion_point(interface_extends:weaviategrpc.SearchReply) - com.google.protobuf.MessageOrBuilder { - } - /** - * Protobuf type {@code weaviategrpc.SearchReply} - */ - public static final class SearchReply extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:weaviategrpc.SearchReply) - SearchReplyOrBuilder { - private static final long serialVersionUID = 0L; - // Use SearchReply.newBuilder() to construct. - private SearchReply(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - private SearchReply() { - } - - @java.lang.Override - @SuppressWarnings({"unused"}) - protected java.lang.Object newInstance( - UnusedPrivateParameter unused) { - return new SearchReply(); - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchReply_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchReply_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.class, io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.Builder.class); - } - - private byte memoizedIsInitialized = -1; - @java.lang.Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - memoizedIsInitialized = 1; - return true; - } - - @java.lang.Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getUnknownFields().writeTo(output); - } - - @java.lang.Override - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - size += getUnknownFields().getSerializedSize(); - memoizedSize = size; - return size; - } - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply)) { - return super.equals(obj); - } - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply other = (io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply) obj; - - if (!getUnknownFields().equals(other.getUnknownFields())) return false; - return true; - } - - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (29 * hash) + getUnknownFields().hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - @java.lang.Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - @java.lang.Override - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code weaviategrpc.SearchReply} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:weaviategrpc.SearchReply) - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReplyOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchReply_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchReply_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.class, io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.Builder.class); - } - - // Construct using io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.newBuilder() - private Builder() { - - } - - private Builder( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - super(parent); - - } - @java.lang.Override - public Builder clear() { - super.clear(); - return this; - } - - @java.lang.Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.internal_static_weaviategrpc_SearchReply_descriptor; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply getDefaultInstanceForType() { - return io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.getDefaultInstance(); - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply build() { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply buildPartial() { - io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply result = new io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply(this); - onBuilt(); - return result; - } - - @java.lang.Override - public Builder clone() { - return super.clone(); - } - @java.lang.Override - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { - return super.setField(field, value); - } - @java.lang.Override - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return super.clearField(field); - } - @java.lang.Override - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return super.clearOneof(oneof); - } - @java.lang.Override - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, java.lang.Object value) { - return super.setRepeatedField(field, index, value); - } - @java.lang.Override - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - java.lang.Object value) { - return super.addRepeatedField(field, value); - } - @java.lang.Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply) { - return mergeFrom((io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply other) { - if (other == io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply.getDefaultInstance()) return this; - this.mergeUnknownFields(other.getUnknownFields()); - onChanged(); - return this; - } - - @java.lang.Override - public final boolean isInitialized() { - return true; - } - - @java.lang.Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - if (extensionRegistry == null) { - throw new java.lang.NullPointerException(); - } - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!super.parseUnknownField(input, extensionRegistry, tag)) { - done = true; // was an endgroup tag - } - break; - } // default: - } // switch (tag) - } // while (!done) - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } finally { - onChanged(); - } // finally - return this; - } - @java.lang.Override - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.setUnknownFields(unknownFields); - } - - @java.lang.Override - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.mergeUnknownFields(unknownFields); - } - - - // @@protoc_insertion_point(builder_scope:weaviategrpc.SearchReply) - } - - // @@protoc_insertion_point(class_scope:weaviategrpc.SearchReply) - private static final io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply(); - } - - public static io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - @java.lang.Override - public SearchReply parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (com.google.protobuf.UninitializedMessageException e) { - throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException(e) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - @java.lang.Override - public io.weaviate.client6.grpc.protocol.v0.WeaviateProtoSearchGet.SearchReply getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - - } - - private static final com.google.protobuf.Descriptors.Descriptor - internal_static_weaviategrpc_SearchRequest_descriptor; - private static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_weaviategrpc_SearchRequest_fieldAccessorTable; - private static final com.google.protobuf.Descriptors.Descriptor - internal_static_weaviategrpc_SearchReply_descriptor; - private static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_weaviategrpc_SearchReply_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n\023v0/search_get.proto\022\014weaviategrpc\"\017\n\rS" + - "earchRequest\"\r\n\013SearchReplyBt\n$io.weavia" + - "te.client6.grpc.protocol.v0B\026WeaviatePro" + - "toSearchGetZ4github.com/weaviate/weaviat" + - "e/grpc/generated;protocolb\006proto3" - }; - descriptor = com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }); - internal_static_weaviategrpc_SearchRequest_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_weaviategrpc_SearchRequest_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_weaviategrpc_SearchRequest_descriptor, - new java.lang.String[] { }); - internal_static_weaviategrpc_SearchReply_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_weaviategrpc_SearchReply_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_weaviategrpc_SearchReply_descriptor, - new java.lang.String[] { }); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/src/main/java/io/weaviate/client6/internal/GRPC.java b/src/main/java/io/weaviate/client6/internal/GRPC.java new file mode 100644 index 000000000..bd9bdd6c1 --- /dev/null +++ b/src/main/java/io/weaviate/client6/internal/GRPC.java @@ -0,0 +1,93 @@ +package io.weaviate.client6.internal; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.Arrays; + +import org.apache.commons.lang3.ArrayUtils; + +import com.google.protobuf.ByteString; + +public class GRPC { + private static final ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN; + + /** Encode Float[] to ByteString. */ + public static ByteString toByteString(Float[] vector) { + if (vector == null || vector.length == 0) { + return ByteString.EMPTY; + } + ByteBuffer buffer = ByteBuffer.allocate(vector.length * Float.BYTES).order(BYTE_ORDER); + Arrays.stream(vector).forEach(buffer::putFloat); + return ByteString.copyFrom(buffer.array()); + } + + /** Encode float[] to ByteString. */ + public static ByteString toByteString(float[] vector) { + ByteBuffer buffer = ByteBuffer.allocate(vector.length * Float.BYTES).order(BYTE_ORDER); + for (float f : vector) { + buffer.putFloat(f); + } + return ByteString.copyFrom(buffer.array()); + } + + /** + * Encode Float[][] to ByteString. + *

+ * The first 2 bytes of the resulting ByteString encode the number of dimensions + * (uint16 / short) followed by concatenated vectors (4 bytes per element). + */ + public static ByteString toByteString(Float[][] vectors) { + if (vectors == null || vectors.length == 0 || vectors[0].length == 0) { + return ByteString.EMPTY; + } + + int n = vectors.length; + short dimensions = (short) vectors[0].length; + int capacity = /* vector dimensions */ Short.BYTES + + /* concatenated elements */ (n * dimensions * Float.BYTES); + ByteBuffer buffer = ByteBuffer.allocate(capacity).order(BYTE_ORDER) + .putShort(dimensions); + Arrays.stream(vectors).forEach(v -> Arrays.stream(v).forEach(buffer::putFloat)); + return ByteString.copyFrom(buffer.array()); + } + + /** + * Decode ByteString into a Float[]. ByteString size must be a multiple of + * {@link Float#BYTES}, throws {@link IllegalArgumentException} otherwise. + */ + public static Float[] fromByteString(ByteString bs) { + if (bs.size() % Float.BYTES != 0) { + throw new IllegalArgumentException( + "byte string size not a multiple of " + String.valueOf(Float.BYTES) + " (Float.BYTES)"); + } + float[] vector = new float[bs.size() / Float.BYTES]; + bs.asReadOnlyByteBuffer().order(BYTE_ORDER).asFloatBuffer().get(vector); + return ArrayUtils.toObject(vector); + } + + /** Decode ByteString into a Float[][]. */ + public static Float[][] fromByteStringMulti(ByteString bs) { + if (bs == null || bs.size() == 0) { + return new Float[0][0]; + } + + ByteBuffer buf = bs.asReadOnlyByteBuffer().order(BYTE_ORDER); + + // Dimensions are encoded in the first 2 bytes. + short dimensions = buf.getShort(); // advances current position + + FloatBuffer fbuf = buf.asFloatBuffer(); + int n = fbuf.remaining() / dimensions; // fbuf size is buf / Float.BYTES + + // Reading from buffer advances current position, + // so we always read from offset=0. + Float[][] vectors = new Float[n][dimensions]; + for (int i = 0; i < n; i++) { + float[] v = new float[dimensions]; + fbuf.get(v, 0, dimensions); + vectors[i] = ArrayUtils.toObject(v); + } + return vectors; + } +} diff --git a/src/main/java/io/weaviate/client6/internal/HttpClient.java b/src/main/java/io/weaviate/client6/internal/HttpClient.java deleted file mode 100644 index 5a25d406a..000000000 --- a/src/main/java/io/weaviate/client6/internal/HttpClient.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.weaviate.client6.internal; - -public class HttpClient { -} diff --git a/src/main/java/io/weaviate/client6/v1/Collection.java b/src/main/java/io/weaviate/client6/v1/Collection.java index dde111118..095c0d4ba 100644 --- a/src/main/java/io/weaviate/client6/v1/Collection.java +++ b/src/main/java/io/weaviate/client6/v1/Collection.java @@ -9,7 +9,7 @@ public class Collection { public final Data data; public Collection(Config config, String collectionName) { - this.query = new Query<>(); + this.query = new Query<>(collectionName, config); this.data = new Data<>(collectionName, config); } } diff --git a/src/main/java/io/weaviate/client6/v1/data/Data.java b/src/main/java/io/weaviate/client6/v1/data/Data.java index d058808ac..6289fa8bc 100644 --- a/src/main/java/io/weaviate/client6/v1/data/Data.java +++ b/src/main/java/io/weaviate/client6/v1/data/Data.java @@ -21,6 +21,7 @@ @AllArgsConstructor public class Data { + // TODO: inject singleton as dependency private static final Gson gson = new Gson(); // TODO: this should be wrapped around in some TypeInspector etc. diff --git a/src/main/java/io/weaviate/client6/v1/query/Metadata.java b/src/main/java/io/weaviate/client6/v1/query/Metadata.java new file mode 100644 index 000000000..1919a9091 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/query/Metadata.java @@ -0,0 +1,12 @@ +package io.weaviate.client6.v1.query; + +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.MetadataRequest; + +/** + * Metadata is the common base for all properties that are requestes as + * "_additional". It is an inteface all metadata properties MUST implement to be + * used in {@link SearchOptions}. + */ +public interface Metadata { + void appendTo(MetadataRequest.Builder metadata); +} diff --git a/src/main/java/io/weaviate/client6/v1/query/MetadataField.java b/src/main/java/io/weaviate/client6/v1/query/MetadataField.java new file mode 100644 index 000000000..fbec8a04c --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/query/MetadataField.java @@ -0,0 +1,28 @@ +package io.weaviate.client6.v1.query; + +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.MetadataRequest; + +/** + * MetadataField are collection properties that can be requested for any object. + */ +public enum MetadataField implements Metadata { + ID, + VECTOR, + DISTANCE; + + // FIXME: ideally, we don't want to surface this method in the public API + // But we might have to, if we want to implement that QueryAppender interface. + public void appendTo(MetadataRequest.Builder metadata) { + switch (this) { + case ID: + metadata.setUuid(true); + break; + case VECTOR: + metadata.setVector(true); + break; + case DISTANCE: + metadata.setDistance(true); + break; + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/query/NearVector.java b/src/main/java/io/weaviate/client6/v1/query/NearVector.java index edd0a26f0..e479e8809 100644 --- a/src/main/java/io/weaviate/client6/v1/query/NearVector.java +++ b/src/main/java/io/weaviate/client6/v1/query/NearVector.java @@ -2,10 +2,32 @@ import java.util.function.Consumer; +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoBaseSearch; +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.SearchRequest; +import io.weaviate.client6.internal.GRPC; + public class NearVector { private final Float[] vector; private final Options options; + void appendTo(SearchRequest.Builder search) { + var nearVector = WeaviateProtoBaseSearch.NearVector.newBuilder(); + + // TODO: we should only add (named) Vectors. + // Since we do not force the users to supply a name when defining an index, + // we also need a way to "get default vector name" from the collection. + // For Map (untyped query handle) we always require the name. + nearVector.setVectorBytes(GRPC.toByteString(vector)); + options.append(search, nearVector); + search.setNearVector(nearVector.build()); + } + + public NearVector(Float[] vector, Consumer options) { + this.options = new Options(); + this.vector = vector; + options.accept(this.options); + } + public static class Options extends QueryOptions { private Float distance; private Float certainty; @@ -19,11 +41,14 @@ public Options certainty(float certainty) { this.certainty = certainty; return this; } - } - public NearVector(Float[] vector, Consumer options) { - this.options = new Options(); - this.vector = vector; - options.accept(this.options); + void append(SearchRequest.Builder search, WeaviateProtoBaseSearch.NearVector.Builder nearVector) { + if (certainty != null) { + nearVector.setCertainty(certainty); + } else if (distance != null) { + nearVector.setDistance(distance); + } + super.appendTo(search); + } } } diff --git a/src/main/java/io/weaviate/client6/v1/query/Query.java b/src/main/java/io/weaviate/client6/v1/query/Query.java index c3b1f938b..1aec5bf59 100644 --- a/src/main/java/io/weaviate/client6/v1/query/Query.java +++ b/src/main/java/io/weaviate/client6/v1/query/Query.java @@ -1,11 +1,122 @@ package io.weaviate.client6.v1.query; +import java.time.OffsetDateTime; +import java.util.Date; +import java.util.List; +import java.util.Map; import java.util.function.Consumer; +import java.util.stream.Collectors; + +import com.google.gson.Gson; + +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.stub.MetadataUtils; +import io.weaviate.client6.Config; +import io.weaviate.client6.grpc.protocol.v1.WeaviateGrpc; +import io.weaviate.client6.grpc.protocol.v1.WeaviateGrpc.WeaviateBlockingStub; +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoProperties.Value; +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.MetadataResult; +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.SearchReply; +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.SearchRequest; +import io.weaviate.client6.internal.GRPC; public class Query { + // TODO: inject singleton as dependency + private static final Gson gson = new Gson(); + + // TODO: this should be wrapped around in some TypeInspector etc. + private final String collectionName; + + // TODO: hide befind an internal HttpClient + private final Config config; + + // TODO: implement Closeable and call grpc.shutdown() on exit + // (probably on a "higher" level); + private WeaviateBlockingStub grpc; + + public Query(String collectionName, Config config) { + this.config = config; + this.collectionName = collectionName; - public SearchResult nearVector(Float[] vector, Consumer options) { + // TODO: add request headers (config.headers + authorization) + this.grpc = WeaviateGrpc.newBlockingStub(buildChannel(config)) + .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(new io.grpc.Metadata())); + } + + public QueryResult nearVector(Float[] vector, Consumer options) { var query = new NearVector(vector, options); + + // TODO: Since we always need to set these values, we migth want to move the + // next block to some factory method. + var req = SearchRequest.newBuilder(); + req.setCollection(collectionName); + req.setUses123Api(true); + req.setUses125Api(true); + req.setUses127Api(true); + + query.appendTo(req); + return search(req.build()); + } + + private QueryResult search(SearchRequest req) { + var reply = grpc.search(req); + return deserializeUntyped(reply); + } + + public QueryResult deserializeUntyped(SearchReply reply) { + List> objects = reply.getResultsList().stream() + .map(res -> { + Map properties = convertProtoMap(res.getProperties().getNonRefProps().getFieldsMap()); + + MetadataResult meta = res.getMetadata(); + var metadata = new QueryResult.SearchObject.QueryMetadata( + meta.getId(), + meta.getDistancePresent() ? meta.getDistance() : null, + GRPC.fromByteString(meta.getVectorBytes())); + // FIXME: rather than doing this unchecked cast, we should deal + // with the ORM and "untyped map" cases explicitly. + return new QueryResult.SearchObject((T) properties, metadata); + }).toList(); + + return new QueryResult(objects); + } + + /** + * Convert Map to Map such that can be + * (de-)serialized by {@link Gson}. + */ + private static Map convertProtoMap(Map map) { + return map.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, e -> convertProtoValue(e.getValue()))); + } + + /** + * Convert protobuf's Value stub to an Object by extracting the first available + * field. The checks are non-exhaustive and only cover text, boolean, and + * integer values. + */ + private static Object convertProtoValue(Value value) { + if (value.hasTextValue()) { + return value.getTextValue(); + } else if (value.hasBoolValue()) { + return value.getBoolValue(); + } else if (value.hasIntValue()) { + return value.getIntValue(); + } else if (value.hasNumberValue()) { + return value.getNumberValue(); + } else if (value.hasDateValue()) { + OffsetDateTime offsetDateTime = OffsetDateTime.parse(value.getDateValue()); + return Date.from(offsetDateTime.toInstant()); + } else { + assert false : "branch not covered"; + } return null; } + + private static ManagedChannel buildChannel(Config config) { + ManagedChannelBuilder channelBuilder = ManagedChannelBuilder.forTarget(config.grpcAddress()); + channelBuilder.usePlaintext(); + return channelBuilder.build(); + } } diff --git a/src/main/java/io/weaviate/client6/v1/query/QueryOptions.java b/src/main/java/io/weaviate/client6/v1/query/QueryOptions.java index 2dae7d7f6..5ae284953 100644 --- a/src/main/java/io/weaviate/client6/v1/query/QueryOptions.java +++ b/src/main/java/io/weaviate/client6/v1/query/QueryOptions.java @@ -1,8 +1,15 @@ package io.weaviate.client6.v1.query; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.apache.commons.lang3.StringUtils; + +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.MetadataRequest; +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.PropertiesRequest; +import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.SearchRequest; + @SuppressWarnings("unchecked") abstract class QueryOptions> { private Integer limit; @@ -11,6 +18,7 @@ abstract class QueryOptions> { private String after; private String consistencyLevel; private List returnProperties = new ArrayList<>(); + private List returnMetadata = new ArrayList<>(); public final SELF limit(Integer limit) { this.limit = limit; @@ -36,4 +44,41 @@ public final SELF consistencyLevel(String consistencyLevel) { this.consistencyLevel = consistencyLevel; return (SELF) this; } + + public final SELF returnMetadata(Metadata... metadata) { + this.returnMetadata = Arrays.asList(metadata); + return (SELF) this; + } + + void appendTo(SearchRequest.Builder search) { + if (limit != null) { + search.setLimit(limit); + } + if (offset != null) { + search.setOffset(offset); + } + if (StringUtils.isNotBlank(after)) { + search.setAfter(after); + } + if (StringUtils.isNotBlank(consistencyLevel)) { + search.setConsistencyLevelValue(Integer.valueOf(consistencyLevel)); + } + if (autocut != null) { + search.setAutocut(autocut); + } + + if (!returnMetadata.isEmpty()) { + var metadata = MetadataRequest.newBuilder(); + returnMetadata.forEach(m -> m.appendTo(metadata)); + search.setMetadata(metadata); + } + + if (!returnProperties.isEmpty()) { + var properties = PropertiesRequest.newBuilder(); + for (String property : returnProperties) { + properties.addNonRefProperties(property); + } + search.setProperties(properties); + } + } } diff --git a/src/main/java/io/weaviate/client6/v1/query/SearchResult.java b/src/main/java/io/weaviate/client6/v1/query/QueryResult.java similarity index 69% rename from src/main/java/io/weaviate/client6/v1/query/SearchResult.java rename to src/main/java/io/weaviate/client6/v1/query/QueryResult.java index 8a487435a..24b0a91e2 100644 --- a/src/main/java/io/weaviate/client6/v1/query/SearchResult.java +++ b/src/main/java/io/weaviate/client6/v1/query/QueryResult.java @@ -6,19 +6,20 @@ import lombok.ToString; @AllArgsConstructor -public class SearchResult { +public class QueryResult { public final List> objects; @AllArgsConstructor public static class SearchObject { public final T properties; - public final SearchMetadata metadata; + public final QueryMetadata metadata; @AllArgsConstructor @ToString - public static class SearchMetadata { + public static class QueryMetadata { String id; Float distance; + // TODO: use Vectors (to handle both Float[] and Float[][]) Float[] vector; } } diff --git a/src/main/proto/v0/batch.proto b/src/main/proto/v0/batch.proto deleted file mode 100644 index 425854711..000000000 --- a/src/main/proto/v0/batch.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -package weaviategrpc; - -option go_package = "github.com/weaviate/weaviate/grpc/generated;protocol"; -option java_package = "io.weaviate.client6.grpc.protocol.v0"; -option java_outer_classname = "WeaviateProtoBatch"; - -message BatchObjectsRequest { -} -message BatchObjectsReply { -} diff --git a/src/main/proto/v0/search_get.proto b/src/main/proto/v0/search_get.proto deleted file mode 100644 index 0ef5105b0..000000000 --- a/src/main/proto/v0/search_get.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -package weaviategrpc; - -option go_package = "github.com/weaviate/weaviate/grpc/generated;protocol"; -option java_package = "io.weaviate.client6.grpc.protocol.v0"; -option java_outer_classname = "WeaviateProtoSearchGet"; - -message SearchRequest { -} -message SearchReply { -} diff --git a/src/main/proto/v0/weaviate.proto b/src/main/proto/v0/weaviate.proto deleted file mode 100644 index e7fc3b822..000000000 --- a/src/main/proto/v0/weaviate.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; - -package weaviategrpc; - -import "v0/batch.proto"; -import "v0/search_get.proto"; - -option go_package = "github.com/weaviate/weaviate/grpc/generated;protocol"; -option java_package = "io.weaviate.client6.grpc.protocol.v0"; -option java_outer_classname = "WeaviateProto"; - -service Weaviate { - rpc Search(SearchRequest) returns (SearchReply) {}; - rpc BatchObjects(BatchObjectsRequest) returns (BatchObjectsReply) {}; -} From 48e4c1cbd3207459a2dc13d007f612ac61518249 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sun, 9 Mar 2025 16:43:01 +0100 Subject: [PATCH 04/17] chore: fix @link-ed class in docstring --- src/main/java/io/weaviate/client6/v1/query/Metadata.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/weaviate/client6/v1/query/Metadata.java b/src/main/java/io/weaviate/client6/v1/query/Metadata.java index 1919a9091..d490ee67f 100644 --- a/src/main/java/io/weaviate/client6/v1/query/Metadata.java +++ b/src/main/java/io/weaviate/client6/v1/query/Metadata.java @@ -5,7 +5,7 @@ /** * Metadata is the common base for all properties that are requestes as * "_additional". It is an inteface all metadata properties MUST implement to be - * used in {@link SearchOptions}. + * used in {@link QueryOptions}. */ public interface Metadata { void appendTo(MetadataRequest.Builder metadata); From 6d4d09dcad2066f392ad69d9ca6a573b087b76d5 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Mon, 10 Mar 2025 14:22:28 +0100 Subject: [PATCH 05/17] refactor: move DTOs to separate files Convert from/to response models in standardized methods. --- .../io/weaviate/client6/WeaviateClient.java | 2 +- .../v1/collections/CollectionDefinition.java | 21 +++++++++ .../{dto => }/CollectionDefinitionDTO.java | 16 +++++-- .../v1/{ => collections}/Collections.java | 12 ++--- .../io/weaviate/client6/v1/data/Data.java | 34 +++----------- .../client6/v1/data/WeaviateObject.java | 47 +++++++------------ .../client6/v1/data/WeaviateObjectDTO.java | 44 +++++++++++++++++ 7 files changed, 107 insertions(+), 69 deletions(-) rename src/main/java/io/weaviate/client6/v1/collections/{dto => }/CollectionDefinitionDTO.java (63%) rename src/main/java/io/weaviate/client6/v1/{ => collections}/Collections.java (79%) create mode 100644 src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java diff --git a/src/main/java/io/weaviate/client6/WeaviateClient.java b/src/main/java/io/weaviate/client6/WeaviateClient.java index e9b6cc06f..6a0cec9d7 100644 --- a/src/main/java/io/weaviate/client6/WeaviateClient.java +++ b/src/main/java/io/weaviate/client6/WeaviateClient.java @@ -1,6 +1,6 @@ package io.weaviate.client6; -import io.weaviate.client6.v1.Collections; +import io.weaviate.client6.v1.collections.Collections; public class WeaviateClient { public final Collections collections; diff --git a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java index b5254e90b..8645d12c9 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java @@ -1,5 +1,8 @@ package io.weaviate.client6.v1.collections; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -7,6 +10,12 @@ import java.util.Map; import java.util.function.Consumer; +import com.google.gson.Gson; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PACKAGE) public class CollectionDefinition { public final String name; public final List properties; @@ -50,4 +59,16 @@ public Configuration vector(String name) { options.accept(this); } } + + // JSON serialization --------------- + static CollectionDefinition fromJson(Gson gson, InputStream input) throws IOException { + try (var r = new InputStreamReader(input)) { + var dto = gson.fromJson(r, CollectionDefinitionDTO.class); + return dto.toCollectionDefinition(); + } + } + + public String toJson(Gson gson) { + return gson.toJson(new CollectionDefinitionDTO(this)); + } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/dto/CollectionDefinitionDTO.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java similarity index 63% rename from src/main/java/io/weaviate/client6/v1/collections/dto/CollectionDefinitionDTO.java rename to src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java index a2f333b7a..205ff736b 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/dto/CollectionDefinitionDTO.java +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java @@ -1,13 +1,14 @@ -package io.weaviate.client6.v1.collections.dto; +package io.weaviate.client6.v1.collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; import com.google.gson.annotations.SerializedName; -import io.weaviate.client6.v1.collections.CollectionDefinition; -import io.weaviate.client6.v1.collections.Property; +import io.weaviate.client6.v1.collections.CollectionDefinition.VectorConfig; public class CollectionDefinitionDTO { @SerializedName("class") @@ -32,4 +33,13 @@ public CollectionDefinitionDTO(CollectionDefinition colDef) { "vectorIndexConfig", index.indexConfiguration())); } } + + CollectionDefinition toCollectionDefinition() { + return new CollectionDefinition( + collection, + properties, + vectorIndices.entrySet().stream() + .collect(Collectors.toMap( + Entry::getKey, entry -> (VectorConfig) entry.getValue()))); + } } diff --git a/src/main/java/io/weaviate/client6/v1/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java similarity index 79% rename from src/main/java/io/weaviate/client6/v1/Collections.java rename to src/main/java/io/weaviate/client6/v1/collections/Collections.java index e9aecb34c..bbe708286 100644 --- a/src/main/java/io/weaviate/client6/v1/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -1,4 +1,4 @@ -package io.weaviate.client6.v1; +package io.weaviate.client6.v1.collections; import java.io.IOException; import java.util.Map; @@ -15,8 +15,7 @@ import com.google.gson.Gson; import io.weaviate.client6.Config; -import io.weaviate.client6.v1.collections.CollectionDefinition; -import io.weaviate.client6.v1.collections.dto.CollectionDefinitionDTO; +import io.weaviate.client6.v1.Collection; import lombok.AllArgsConstructor; @AllArgsConstructor @@ -24,17 +23,16 @@ public class Collections { // TODO: hide befind an internal HttpClient private final Config config; + // TODO: use singleton configured in one place private static final Gson gson = new Gson(); public void create(String name, Consumer options) throws IOException { var collection = new CollectionDefinition(name, options); - var body = new CollectionDefinitionDTO(collection); try (CloseableHttpClient httpclient = HttpClients.createDefault()) { - String jsonBody = gson.toJson(body); ClassicHttpRequest httpPost = ClassicRequestBuilder .post(config.baseUrl() + "/schema") - .setEntity(jsonBody, ContentType.APPLICATION_JSON) + .setEntity(collection.toJson(gson), ContentType.APPLICATION_JSON) .build(); httpclient.execute(httpPost, response -> { @@ -48,7 +46,7 @@ public void create(String name, Consumer opt } } - public Collection> use(String name) { + public io.weaviate.client6.v1.Collection> use(String name) { return new Collection<>(config, name); } } diff --git a/src/main/java/io/weaviate/client6/v1/data/Data.java b/src/main/java/io/weaviate/client6/v1/data/Data.java index 6289fa8bc..ea0b42f4c 100644 --- a/src/main/java/io/weaviate/client6/v1/data/Data.java +++ b/src/main/java/io/weaviate/client6/v1/data/Data.java @@ -1,7 +1,6 @@ package io.weaviate.client6.v1.data; import java.io.IOException; -import java.io.InputStreamReader; import java.util.Optional; import java.util.function.Consumer; @@ -13,7 +12,6 @@ import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; -import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import io.weaviate.client6.Config; @@ -31,11 +29,11 @@ public class Data { private final Config config; public WeaviateObject insert(T object, Consumer metadata) throws IOException { - var body = new WeaviateObject<>(collectionName, object, metadata).toRequestObject(); + var body = new WeaviateObject<>(collectionName, object, metadata); try (CloseableHttpClient httpclient = HttpClients.createDefault()) { ClassicHttpRequest httpPost = ClassicRequestBuilder .post(config.baseUrl() + "/objects") - .setEntity(gson.toJson(body), ContentType.APPLICATION_JSON) + .setEntity(body.toJson(gson), ContentType.APPLICATION_JSON) .build(); return httpclient.execute(httpPost, response -> { @@ -45,12 +43,7 @@ public WeaviateObject insert(T object, Consumer metadata) thr throw new RuntimeException("HTTP " + response.getCode() + ": " + message); } - try (var r = new InputStreamReader(entity.getContent())) { - WeaviateObject.RequestObject inserted = gson.fromJson(r, - new TypeToken>() { - }.getType()); - return inserted.toWeaviateObject(); - } + return WeaviateObject.fromJson(gson, entity.getContent()); }); } } @@ -65,23 +58,10 @@ public Optional> get(String id) throws IOException { if (response.getCode() == HttpStatus.SC_NOT_FOUND) { return Optional.empty(); } - var json = EntityUtils.toString(response.getEntity()); - WeaviateObject.RequestObject object = gson.fromJson(json, - new TypeToken>() { - }.getType()); - if (object == null) { - return Optional.empty(); - } - return Optional.of(object.toWeaviateObject()); - // try (var r = new InputStreamReader(response.getEntity().getContent())) { - // WeaviateObject.RequestObject object = gson.fromJson(r, - // new TypeToken>() { - // }.getType()); - // if (object == null) { - // return Optional.empty(); - // } - // return Optional.of(object.toWeaviateObject()); - // } + + WeaviateObject object = WeaviateObject.fromJson( + gson, response.getEntity().getContent()); + return Optional.ofNullable(object); }); } } diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java index ea71c214c..33dba742a 100644 --- a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java @@ -1,17 +1,19 @@ package io.weaviate.client6.v1.data; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.function.Consumer; -import com.google.gson.annotations.SerializedName; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import lombok.AccessLevel; import lombok.AllArgsConstructor; // TODO: unify this with collections.SearchObject -@AllArgsConstructor +@AllArgsConstructor(access = AccessLevel.PACKAGE) public class WeaviateObject { public final String collection; public final T properties; @@ -31,33 +33,16 @@ public static class Metadata { this.metadata = new Metadata(metadata.id, metadata.vectors); } - RequestObject toRequestObject() { - return new RequestObject(collection, metadata.id, properties, metadata.vectors.asMap()); + // JSON serialization ---------------- + public static WeaviateObject fromJson(Gson gson, InputStream input) throws IOException { + try (var r = new InputStreamReader(input)) { + WeaviateObjectDTO dto = gson.fromJson(r, new TypeToken>() { + }.getType()); + return dto.toWeaviateObject(); + } } - @AllArgsConstructor - static class RequestObject { - @SerializedName("class") - public String collection; - @SerializedName("id") - public String id; - @SerializedName("properties") - public T properties; - @SerializedName("vectors") - public Map vectors; - - WeaviateObject toWeaviateObject() { - Map arrayVectors = new HashMap<>(); - for (var entry : vectors.entrySet()) { - var value = (ArrayList) entry.getValue(); - var vector = new Float[value.size()]; - int i = 0; - for (var v : value) { - vector[i++] = v.floatValue(); - } - arrayVectors.put(entry.getKey(), vector); - } - return new WeaviateObject(collection, properties, new Metadata(id, new Vectors(arrayVectors))); - } + public String toJson(Gson gson) { + return gson.toJson(new WeaviateObjectDTO<>(this)); } } diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java new file mode 100644 index 000000000..b8d24804c --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java @@ -0,0 +1,44 @@ +package io.weaviate.client6.v1.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.annotations.SerializedName; + +import io.weaviate.client6.v1.data.WeaviateObject.Metadata; + +class WeaviateObjectDTO { + @SerializedName("class") + String collection; + @SerializedName("id") + String id; + @SerializedName("properties") + T properties; + @SerializedName("vectors") + Map vectors; + + WeaviateObjectDTO(WeaviateObject object) { + this.collection = object.collection; + this.properties = object.properties; + + if (object.metadata != null) { + this.id = object.metadata.id; + this.vectors = object.metadata.vectors.asMap(); + } + } + + WeaviateObject toWeaviateObject() { + Map arrayVectors = new HashMap<>(); + for (var entry : vectors.entrySet()) { + var value = (ArrayList) entry.getValue(); + var vector = new Float[value.size()]; + int i = 0; + for (var v : value) { + vector[i++] = v.floatValue(); + } + arrayVectors.put(entry.getKey(), vector); + } + return new WeaviateObject(collection, properties, new Metadata(id, new Vectors(arrayVectors))); + } +} From a91470218caa4b7ef40a5e6836a8402db877160b Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Mon, 10 Mar 2025 18:44:48 +0100 Subject: [PATCH 06/17] refactor: allow chainging multiple Vectors.of calls --- .../io/weaviate/client6/v1/DataITest.java | 1 - .../v1/query/NearVectorQueryITest.java | 2 +- .../java/io/weaviate/client6/v1/Vectors.java | 126 ++++++++++++++++++ .../client6/v1/collections/Collections.java | 1 + .../client6/v1/data/CustomMetadata.java | 28 ---- .../io/weaviate/client6/v1/data/Data.java | 4 +- .../io/weaviate/client6/v1/data/Metadata.java | 45 +++++++ .../io/weaviate/client6/v1/data/Vectors.java | 86 ------------ .../client6/v1/data/WeaviateObject.java | 11 +- .../client6/v1/data/WeaviateObjectDTO.java | 2 +- 10 files changed, 178 insertions(+), 128 deletions(-) create mode 100644 src/main/java/io/weaviate/client6/v1/Vectors.java delete mode 100644 src/main/java/io/weaviate/client6/v1/data/CustomMetadata.java create mode 100644 src/main/java/io/weaviate/client6/v1/data/Metadata.java delete mode 100644 src/main/java/io/weaviate/client6/v1/data/Vectors.java diff --git a/src/it/java/io/weaviate/client6/v1/DataITest.java b/src/it/java/io/weaviate/client6/v1/DataITest.java index 865954290..31952bedf 100644 --- a/src/it/java/io/weaviate/client6/v1/DataITest.java +++ b/src/it/java/io/weaviate/client6/v1/DataITest.java @@ -11,7 +11,6 @@ import io.weaviate.ConcurrentTest; import io.weaviate.client6.WeaviateClient; import io.weaviate.client6.v1.collections.Property; -import io.weaviate.client6.v1.data.Vectors; import io.weaviate.containers.Container; public class DataITest extends ConcurrentTest { diff --git a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java index dbefa2bdd..d490e8fb2 100644 --- a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java +++ b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java @@ -12,7 +12,7 @@ import io.weaviate.ConcurrentTest; import io.weaviate.client6.WeaviateClient; -import io.weaviate.client6.v1.data.Vectors; +import io.weaviate.client6.v1.Vectors; import io.weaviate.containers.Container; public class NearVectorQueryITest extends ConcurrentTest { diff --git a/src/main/java/io/weaviate/client6/v1/Vectors.java b/src/main/java/io/weaviate/client6/v1/Vectors.java new file mode 100644 index 000000000..99cb0780b --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/Vectors.java @@ -0,0 +1,126 @@ +package io.weaviate.client6.v1; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Vectors is an abstraction over named vectors. + * It may contain both 1-dimensional and 2-dimensional vectors. + */ +public class Vectors { + private static final String DEFAULT = "default"; + + private Float[] legacyVector; + private Map namedVectors; + + public Float[] getSingle(String name) { + return (Float[]) namedVectors.get(name); + } + + public Float[] getDefaultSingle() { + return getSingle(DEFAULT); + } + + @SuppressWarnings("unchecked") + public Optional getSingle() { + return (Optional) getOnly(); + } + + public Float[][] getMulti(String name) { + return (Float[][]) namedVectors.get(name); + } + + public Float[][] getDefaultMulti() { + return getMulti(DEFAULT); + } + + @SuppressWarnings("unchecked") + public Optional getMulti() { + return (Optional) getOnly(); + } + + public Optional getUnnamed() { + return Optional.ofNullable(legacyVector); + } + + private Optional getOnly() { + if (namedVectors == null || namedVectors.isEmpty() || namedVectors.size() > 1) { + return Optional.empty(); + } + return Optional.ofNullable(namedVectors.values().iterator().next()); + } + + public Map asMap() { + return Map.copyOf(namedVectors); + } + + /** Creates Vectors with a single unnamed vector. */ + private Vectors(Float[] vector) { + this.legacyVector = vector; + this.namedVectors = Map.of(); + } + + /** Creates immutable set of vectors. */ + public Vectors(Map vectors) { + this.namedVectors = Collections.unmodifiableMap(vectors); + } + + /** + * Pass legacy unnamed vector. + * Multi-vectors can only be passed as named vectors. + */ + public static Vectors unnamed(Float[] vector) { + return new Vectors(vector); + } + + public static Vectors.Builder of(Float[] vector) { + return Vectors.of(DEFAULT, vector); + } + + public static Vectors.Builder of(Float[][] vector) { + return Vectors.of(DEFAULT, vector); + } + + public static Vectors.Builder of(String name, Float[] vector) { + return new Vectors.Builder(name, vector); + } + + public static Vectors.Builder of(String name, Float[][] vector) { + return new Vectors.Builder(name, vector); + } + + public static class Builder { + private Map namedVectors = new HashMap<>(); + + // Hide this constructor; + private Builder() { + } + + public Builder(String name, Float[] vector) { + this.namedVectors.put(name, vector); + } + + public Builder(String name, Float[][] vector) { + this.namedVectors.put(name, vector); + } + + public Builder of(String name, Float[] vector) { + this.namedVectors.put(name, vector); + return this; + } + + public Builder of(String name, Float[][] vector) { + this.namedVectors.put(name, vector); + return this; + } + + // If we could bring both Data and Query methods in one package, + // then we wouldn't need to expose this method. + // Alternatively, of course, we go with Tucked Builders all the way. + public Vectors build() { + return new Vectors(namedVectors); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java index bbe708286..5dc92f08b 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -15,6 +15,7 @@ import com.google.gson.Gson; import io.weaviate.client6.Config; +import io.weaviate.client6.internal.DtoTypeAdapterFactory; import io.weaviate.client6.v1.Collection; import lombok.AllArgsConstructor; diff --git a/src/main/java/io/weaviate/client6/v1/data/CustomMetadata.java b/src/main/java/io/weaviate/client6/v1/data/CustomMetadata.java deleted file mode 100644 index 0c4c7bc81..000000000 --- a/src/main/java/io/weaviate/client6/v1/data/CustomMetadata.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.weaviate.client6.v1.data; - -import java.util.Map; -import java.util.function.Consumer; - -public class CustomMetadata { - String id; - Vectors vectors; - - public CustomMetadata id(String id) { - this.id = id; - return this; - } - - public CustomMetadata vectors(Vectors vectors) { - this.vectors = vectors; - return this; - } - - public CustomMetadata vectors(Map vectors) { - this.vectors = new Vectors(vectors); - return this; - } - - CustomMetadata(Consumer options) { - options.accept(this); - } -} diff --git a/src/main/java/io/weaviate/client6/v1/data/Data.java b/src/main/java/io/weaviate/client6/v1/data/Data.java index ea0b42f4c..1a53c92cd 100644 --- a/src/main/java/io/weaviate/client6/v1/data/Data.java +++ b/src/main/java/io/weaviate/client6/v1/data/Data.java @@ -28,8 +28,8 @@ public class Data { // TODO: hide befind an internal HttpClient private final Config config; - public WeaviateObject insert(T object, Consumer metadata) throws IOException { - var body = new WeaviateObject<>(collectionName, object, metadata); + public WeaviateObject insert(T object, Consumer options) throws IOException { + var body = new WeaviateObject<>(collectionName, object, options); try (CloseableHttpClient httpclient = HttpClients.createDefault()) { ClassicHttpRequest httpPost = ClassicRequestBuilder .post(config.baseUrl() + "/objects") diff --git a/src/main/java/io/weaviate/client6/v1/data/Metadata.java b/src/main/java/io/weaviate/client6/v1/data/Metadata.java new file mode 100644 index 000000000..e1972704a --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/Metadata.java @@ -0,0 +1,45 @@ + +package io.weaviate.client6.v1.data; + +import java.util.function.Consumer; + +import io.weaviate.client6.v1.Vectors; + +public class Metadata { + public final String id; + public final Vectors vectors; + + Metadata(String id, Vectors vectors) { + this(m -> m.id(id).vectors(vectors)); + } + + Metadata(Consumer options) { + var opt = new Options(options); + + this.id = opt.id; + this.vectors = opt.vectors; + } + + public static class Options { + public String id; + public Vectors vectors; + + public Options id(String id) { + this.id = id; + return this; + } + + public Options vectors(Vectors vectors) { + this.vectors = vectors; + return this; + } + + public Options vectors(Vectors.Builder vectors) { + return vectors(vectors.build()); + } + + Options(Consumer options) { + options.accept(this); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/data/Vectors.java b/src/main/java/io/weaviate/client6/v1/data/Vectors.java deleted file mode 100644 index 9a4fab29e..000000000 --- a/src/main/java/io/weaviate/client6/v1/data/Vectors.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.weaviate.client6.v1.data; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Vectors is an abstraction over named vectors. - * It may contain both 1-dimensional and 2-dimensional vectors. - */ -public class Vectors { - private static final String DEFAULT = "default"; - - private Map namedVectors; - - public static Vectors of(Float[] vector) { - return new Vectors(vector); - } - - public static Vectors of(Float[][] vector) { - return new Vectors(vector); - } - - public static Vectors of(String name, Float[] vector) { - return new Vectors(name, vector); - } - - public static Vectors of(String name, Float[][] vector) { - return new Vectors(name, vector); - } - - /** Creates immutable set of vectors. */ - Vectors(Map vectors) { - this.namedVectors = Collections.unmodifiableMap(vectors); - } - - /** Creates immutable set of vectors. */ - private Vectors(Float[] vector) { - this.namedVectors = Map.of(DEFAULT, vector); - } - - /** Creates immutable set of vectors. */ - private Vectors(Float[][] vector) { - this.namedVectors = Map.of(DEFAULT, vector); - } - - /** Creates extendable set of vectors. */ - private Vectors(String name, Object vector) { - this(); - this.namedVectors.put(name, vector); - } - - Vectors() { - this.namedVectors = new HashMap<>(); - } - - @SuppressWarnings("unchecked") - public Optional getSingle() { - return (Optional) getOnly(); - } - - public Optional getSingle(String name) { - return null; - } - - @SuppressWarnings("unchecked") - public Optional getMulti() { - return (Optional) getOnly(); - } - - public Optional getMulti(String name) { - return null; - } - - private Optional getOnly() { - if (namedVectors == null || namedVectors.isEmpty() || namedVectors.size() > 1) { - return Optional.empty(); - } - return Optional.ofNullable(namedVectors.values().iterator().next()); - } - - Map asMap() { - return Map.copyOf(namedVectors); - } -} diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java index 33dba742a..f9c309ce7 100644 --- a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java @@ -19,18 +19,11 @@ public class WeaviateObject { public final T properties; public final Metadata metadata; - @AllArgsConstructor - public static class Metadata { - public final String id; - public final Vectors vectors; - } - - WeaviateObject(String collection, T properties, Consumer options) { - var metadata = new CustomMetadata(options); + WeaviateObject(String collection, T properties, Consumer options) { this.collection = collection; this.properties = properties; - this.metadata = new Metadata(metadata.id, metadata.vectors); + this.metadata = new Metadata(options); } // JSON serialization ---------------- diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java index b8d24804c..b429f6b7c 100644 --- a/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java @@ -6,7 +6,7 @@ import com.google.gson.annotations.SerializedName; -import io.weaviate.client6.v1.data.WeaviateObject.Metadata; +import io.weaviate.client6.v1.Vectors; class WeaviateObjectDTO { @SerializedName("class") From 21621475320c0e97858bb789a6c4f9ff14f2df0c Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 11 Mar 2025 11:14:30 +0100 Subject: [PATCH 07/17] refactor: define strongly type factory methods for Vectors --- .../v1/query/NearVectorQueryITest.java | 1 + .../weaviate/client6/v1/ObjectMetadata.java | 68 ++++++++++++++ .../java/io/weaviate/client6/v1/Vectors.java | 66 ++++++------- .../client6/v1/collections/Collections.java | 1 - .../io/weaviate/client6/v1/data/Data.java | 3 +- .../io/weaviate/client6/v1/data/Metadata.java | 45 --------- .../client6/v1/data/WeaviateObject.java | 7 +- .../client6/v1/data/WeaviateObjectDTO.java | 3 +- .../client6/v1/ObjectMetadataTest.java | 93 +++++++++++++++++++ 9 files changed, 203 insertions(+), 84 deletions(-) create mode 100644 src/main/java/io/weaviate/client6/v1/ObjectMetadata.java delete mode 100644 src/main/java/io/weaviate/client6/v1/data/Metadata.java create mode 100644 src/test/java/io/weaviate/client6/v1/ObjectMetadataTest.java diff --git a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java index d490e8fb2..97a7c7ea2 100644 --- a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java +++ b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java @@ -65,6 +65,7 @@ private static Map createVectors(int n) throws IOException { Map.of(), metadata -> metadata .id(randomUUID()) + .vectors(Vectors.unnamed(vector)) .vectors(Vectors.of(VECTOR_INDEX, vector))); created.put(object.metadata.id, vector); diff --git a/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java b/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java new file mode 100644 index 000000000..4af13ac7a --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java @@ -0,0 +1,68 @@ +package io.weaviate.client6.v1; + +import java.util.function.Consumer; + +public class ObjectMetadata { + public final String id; + public final Vectors vectors; + + // ObjectMetadata(String id, Vectors vectors) { + // this(m -> m.id(id).vectors(vectors)); + // } + + public ObjectMetadata(String id, Vectors vectors) { + this.id = id; + this.vectors = vectors; + } + + public ObjectMetadata(Consumer options) { + var opt = new Builder(options); + + this.id = opt.id; + this.vectors = opt.vectors; + } + + public static class Builder { + public String id; + public Vectors vectors; + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder vectors(Vectors vectors) { + this.vectors = vectors; + return this; + } + + public Builder vectors(Float[] vector) { + this.vectors = Vectors.of(vector); + return this; + } + + public Builder vectors(Float[][] vector) { + this.vectors = Vectors.of(vector); + return this; + } + + public Builder vectors(String name, Float[] vector) { + this.vectors = Vectors.of(name, vector); + return this; + } + + public Builder vectors(String name, Float[][] vector) { + this.vectors = Vectors.of(name, vector); + return this; + } + + public Builder vectors(Consumer named) { + this.vectors = new Vectors(named); + return this; + } + + private Builder(Consumer options) { + options.accept(this); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/Vectors.java b/src/main/java/io/weaviate/client6/v1/Vectors.java index 99cb0780b..4d781fa91 100644 --- a/src/main/java/io/weaviate/client6/v1/Vectors.java +++ b/src/main/java/io/weaviate/client6/v1/Vectors.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.function.Consumer; /** * Vectors is an abstraction over named vectors. @@ -12,7 +13,7 @@ public class Vectors { private static final String DEFAULT = "default"; - private Float[] legacyVector; + private Float[] unnamedVector; private Map namedVectors; public Float[] getSingle(String name) { @@ -42,7 +43,7 @@ public Optional getMulti() { } public Optional getUnnamed() { - return Optional.ofNullable(legacyVector); + return Optional.ofNullable(unnamedVector); } private Optional getOnly() { @@ -58,15 +59,25 @@ public Map asMap() { /** Creates Vectors with a single unnamed vector. */ private Vectors(Float[] vector) { - this.legacyVector = vector; - this.namedVectors = Map.of(); + this(Map.of()); + this.unnamedVector = vector; + } + + /** Creates Vectors with one named vector. */ + private Vectors(String name, Object vector) { + this.namedVectors = Map.of(name, vector); } /** Creates immutable set of vectors. */ - public Vectors(Map vectors) { + private Vectors(Map vectors) { this.namedVectors = Collections.unmodifiableMap(vectors); } + public Vectors(Consumer options) { + var vectors = new NamedVectors(options); + this.namedVectors = vectors.namedVectors; + } + /** * Pass legacy unnamed vector. * Multi-vectors can only be passed as named vectors. @@ -75,52 +86,41 @@ public static Vectors unnamed(Float[] vector) { return new Vectors(vector); } - public static Vectors.Builder of(Float[] vector) { - return Vectors.of(DEFAULT, vector); + public static Vectors of(Float[] vector) { + return new Vectors(DEFAULT, vector); } - public static Vectors.Builder of(Float[][] vector) { - return Vectors.of(DEFAULT, vector); + public static Vectors of(Float[][] vector) { + return new Vectors(DEFAULT, vector); } - public static Vectors.Builder of(String name, Float[] vector) { - return new Vectors.Builder(name, vector); + public static Vectors of(String name, Float[] vector) { + return new Vectors(name, vector); } - public static Vectors.Builder of(String name, Float[][] vector) { - return new Vectors.Builder(name, vector); + public static Vectors of(String name, Float[][] vector) { + return new Vectors(name, vector); } - public static class Builder { - private Map namedVectors = new HashMap<>(); - - // Hide this constructor; - private Builder() { - } - - public Builder(String name, Float[] vector) { - this.namedVectors.put(name, vector); - } + public static Vectors of(Map vectors) { + return new Vectors(vectors); + } - public Builder(String name, Float[][] vector) { - this.namedVectors.put(name, vector); - } + public static class NamedVectors { + private Map namedVectors = new HashMap<>(); - public Builder of(String name, Float[] vector) { + public NamedVectors vector(String name, Float[] vector) { this.namedVectors.put(name, vector); return this; } - public Builder of(String name, Float[][] vector) { + public NamedVectors vector(String name, Float[][] vector) { this.namedVectors.put(name, vector); return this; } - // If we could bring both Data and Query methods in one package, - // then we wouldn't need to expose this method. - // Alternatively, of course, we go with Tucked Builders all the way. - public Vectors build() { - return new Vectors(namedVectors); + NamedVectors(Consumer options) { + options.accept(this); } } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java index 5dc92f08b..bbe708286 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -15,7 +15,6 @@ import com.google.gson.Gson; import io.weaviate.client6.Config; -import io.weaviate.client6.internal.DtoTypeAdapterFactory; import io.weaviate.client6.v1.Collection; import lombok.AllArgsConstructor; diff --git a/src/main/java/io/weaviate/client6/v1/data/Data.java b/src/main/java/io/weaviate/client6/v1/data/Data.java index 1a53c92cd..03e6dfad8 100644 --- a/src/main/java/io/weaviate/client6/v1/data/Data.java +++ b/src/main/java/io/weaviate/client6/v1/data/Data.java @@ -15,6 +15,7 @@ import com.google.gson.Gson; import io.weaviate.client6.Config; +import io.weaviate.client6.v1.ObjectMetadata; import lombok.AllArgsConstructor; @AllArgsConstructor @@ -28,7 +29,7 @@ public class Data { // TODO: hide befind an internal HttpClient private final Config config; - public WeaviateObject insert(T object, Consumer options) throws IOException { + public WeaviateObject insert(T object, Consumer options) throws IOException { var body = new WeaviateObject<>(collectionName, object, options); try (CloseableHttpClient httpclient = HttpClients.createDefault()) { ClassicHttpRequest httpPost = ClassicRequestBuilder diff --git a/src/main/java/io/weaviate/client6/v1/data/Metadata.java b/src/main/java/io/weaviate/client6/v1/data/Metadata.java deleted file mode 100644 index e1972704a..000000000 --- a/src/main/java/io/weaviate/client6/v1/data/Metadata.java +++ /dev/null @@ -1,45 +0,0 @@ - -package io.weaviate.client6.v1.data; - -import java.util.function.Consumer; - -import io.weaviate.client6.v1.Vectors; - -public class Metadata { - public final String id; - public final Vectors vectors; - - Metadata(String id, Vectors vectors) { - this(m -> m.id(id).vectors(vectors)); - } - - Metadata(Consumer options) { - var opt = new Options(options); - - this.id = opt.id; - this.vectors = opt.vectors; - } - - public static class Options { - public String id; - public Vectors vectors; - - public Options id(String id) { - this.id = id; - return this; - } - - public Options vectors(Vectors vectors) { - this.vectors = vectors; - return this; - } - - public Options vectors(Vectors.Builder vectors) { - return vectors(vectors.build()); - } - - Options(Consumer options) { - options.accept(this); - } - } -} diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java index f9c309ce7..8aa68d107 100644 --- a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java @@ -8,6 +8,7 @@ import com.google.common.reflect.TypeToken; import com.google.gson.Gson; +import io.weaviate.client6.v1.ObjectMetadata; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -17,13 +18,13 @@ public class WeaviateObject { public final String collection; public final T properties; - public final Metadata metadata; + public final ObjectMetadata metadata; - WeaviateObject(String collection, T properties, Consumer options) { + WeaviateObject(String collection, T properties, Consumer options) { this.collection = collection; this.properties = properties; - this.metadata = new Metadata(options); + this.metadata = new ObjectMetadata(options); } // JSON serialization ---------------- diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java index b429f6b7c..3767a7abc 100644 --- a/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java @@ -6,6 +6,7 @@ import com.google.gson.annotations.SerializedName; +import io.weaviate.client6.v1.ObjectMetadata; import io.weaviate.client6.v1.Vectors; class WeaviateObjectDTO { @@ -39,6 +40,6 @@ WeaviateObject toWeaviateObject() { } arrayVectors.put(entry.getKey(), vector); } - return new WeaviateObject(collection, properties, new Metadata(id, new Vectors(arrayVectors))); + return new WeaviateObject(collection, properties, new ObjectMetadata(id, Vectors.of(arrayVectors))); } } diff --git a/src/test/java/io/weaviate/client6/v1/ObjectMetadataTest.java b/src/test/java/io/weaviate/client6/v1/ObjectMetadataTest.java new file mode 100644 index 000000000..549ae24a9 --- /dev/null +++ b/src/test/java/io/weaviate/client6/v1/ObjectMetadataTest.java @@ -0,0 +1,93 @@ +package io.weaviate.client6.v1; + +import java.util.Optional; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +public class ObjectMetadataTest { + + @Test + public final void testMetadata_id() { + var metadata = new ObjectMetadata(m -> m.id("object-1")); + Assertions.assertThat(metadata.id) + .as("object id").isEqualTo("object-1"); + } + + @Test + public final void testVectorsMetadata_unnamed() { + Float[] vector = { 1f, 2f, 3f }; + var metadata = new ObjectMetadata(m -> m.vectors(Vectors.unnamed(vector))); + + Assertions.assertThat(metadata.vectors) + .as("unnamed vector").isNotNull() + .returns(Optional.of(vector), Vectors::getUnnamed) + .returns(Optional.empty(), Vectors::getSingle); + } + + @Test + public final void testVectorsMetadata_default() { + Float[] vector = { 1f, 2f, 3f }; + var metadata = new ObjectMetadata(m -> m.vectors(vector)); + + Assertions.assertThat(metadata.vectors) + .as("default vector").isNotNull() + .returns(vector, Vectors::getDefaultSingle) + .returns(Optional.of(vector), Vectors::getSingle) + .returns(Optional.empty(), Vectors::getUnnamed); + } + + @Test + public final void testVectorsMetadata_default_2d() { + Float[][] vector = { { 1f, 2f, 3f }, { 1f, 2f, 3f } }; + var metadata = new ObjectMetadata(m -> m.vectors(vector)); + + Assertions.assertThat(metadata.vectors) + .as("default 2d vector").isNotNull() + .returns(vector, Vectors::getDefaultMulti) + .returns(Optional.of(vector), Vectors::getMulti) + .returns(Optional.empty(), Vectors::getUnnamed); + } + + @Test + public final void testVectorsMetadata_named() { + Float[] vector = { 1f, 2f, 3f }; + var metadata = new ObjectMetadata(m -> m.vectors("vector-1", vector)); + + Assertions.assertThat(metadata.vectors) + .as("named vector").isNotNull() + .returns(vector, v -> v.getSingle("vector-1")) + .returns(Optional.of(vector), Vectors::getSingle) + .returns(null, Vectors::getDefaultSingle); + } + + @Test + public final void testVectorsMetadata_named_2d() { + Float[][] vector = { { 1f, 2f, 3f }, { 1f, 2f, 3f } }; + var metadata = new ObjectMetadata(m -> m.vectors("vector-1", vector)); + + Assertions.assertThat(metadata.vectors) + .as("named 2d vector").isNotNull() + .returns(vector, v -> v.getMulti("vector-1")) + .returns(Optional.of(vector), Vectors::getMulti) + .returns(null, Vectors::getDefaultMulti); + } + + @Test + public final void testVectorsMetadata_multiple_named() { + Float[][] vector_1 = { { 1f, 2f, 3f }, { 1f, 2f, 3f } }; + Float[] vector_2 = { 4f, 5f, 6f }; + var metadata = new ObjectMetadata(m -> m.vectors( + named -> named + .vector("vector-1", vector_1) + .vector("vector-2", vector_2))); + + Assertions.assertThat(metadata.vectors) + .as("multiple named vectors").isNotNull() + .returns(vector_1, v -> v.getMulti("vector-1")) + .returns(vector_2, v -> v.getSingle("vector-2")) + .returns(Optional.empty(), Vectors::getMulti) + .returns(Optional.empty(), Vectors::getSingle) + .returns(null, Vectors::getDefaultMulti); + } +} From 56ddb00174059e8b43b3675c382f4ec1b25f93a2 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 11 Mar 2025 18:07:23 +0100 Subject: [PATCH 08/17] refactor: define strongly type factory methods for vector index configuration Gson TypeAdapters in Collections.java are WIP. --- .../io/weaviate/client6/v1/DataITest.java | 6 +- .../v1/query/NearVectorQueryITest.java | 4 +- .../weaviate/client6/v1/ObjectMetadata.java | 2 +- .../java/io/weaviate/client6/v1/Vectors.java | 6 +- .../v1/collections/CollectionDefinition.java | 47 +++++------- .../collections/CollectionDefinitionDTO.java | 29 ++------ .../client6/v1/collections/Collections.java | 51 ++++++++++++- .../weaviate/client6/v1/collections/HNSW.java | 33 +++++++++ .../v1/collections/NoneVectorIndex.java | 12 --- .../v1/collections/NoneVectorizer.java | 10 +++ .../client6/v1/collections/VectorIndex.java | 39 ++++++++++ .../client6/v1/collections/Vectorizer.java | 8 ++ .../client6/v1/collections/Vectors.java | 74 +++++++++++++++++++ .../client6/v1/collections/VectorsTesa.java | 34 +++++++++ 14 files changed, 281 insertions(+), 74 deletions(-) create mode 100644 src/main/java/io/weaviate/client6/v1/collections/HNSW.java delete mode 100644 src/main/java/io/weaviate/client6/v1/collections/NoneVectorIndex.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/NoneVectorizer.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/Vectorizer.java create mode 100644 src/main/java/io/weaviate/client6/v1/collections/Vectors.java create mode 100644 src/test/java/io/weaviate/client6/v1/collections/VectorsTesa.java diff --git a/src/it/java/io/weaviate/client6/v1/DataITest.java b/src/it/java/io/weaviate/client6/v1/DataITest.java index 31952bedf..a27ee08b0 100644 --- a/src/it/java/io/weaviate/client6/v1/DataITest.java +++ b/src/it/java/io/weaviate/client6/v1/DataITest.java @@ -11,12 +11,14 @@ import io.weaviate.ConcurrentTest; import io.weaviate.client6.WeaviateClient; import io.weaviate.client6.v1.collections.Property; +import io.weaviate.client6.v1.collections.VectorIndex; import io.weaviate.containers.Container; public class DataITest extends ConcurrentTest { private static WeaviateClient client = Container.WEAVIATE.getClient(); private static final String COLLECTION = unique("Things"); + private static final String VECTOR_INDEX = "bring_your_own"; @BeforeClass public static void beforeAll() throws IOException { @@ -31,7 +33,7 @@ public void testCreateGetDelete() throws IOException { things.data.insert(Map.of("username", "john doe"), metadata -> metadata .id(id) - .vectors(Vectors.of("bring_your_own", vector))); + .vectors(Vectors.of(VECTOR_INDEX, vector))); var object = things.data.get(id); Assertions.assertThat(object) @@ -58,6 +60,6 @@ private static void createTestCollection() throws IOException { client.collections.create(COLLECTION, col -> col .properties(Property.text("username")) - .vector("bring_your_own")); + .vector(VECTOR_INDEX, VectorIndex.hnsw())); } } diff --git a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java index 97a7c7ea2..0c65e8bd6 100644 --- a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java +++ b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java @@ -13,6 +13,7 @@ import io.weaviate.ConcurrentTest; import io.weaviate.client6.WeaviateClient; import io.weaviate.client6.v1.Vectors; +import io.weaviate.client6.v1.collections.VectorIndex; import io.weaviate.containers.Container; public class NearVectorQueryITest extends ConcurrentTest { @@ -80,6 +81,7 @@ private static Map createVectors(int n) throws IOException { * @throws IOException */ private static void createTestCollection() throws IOException { - client.collections.create(COLLECTION, col -> col.vector(VECTOR_INDEX)); + client.collections.create(COLLECTION, cfg -> cfg + .vector(VECTOR_INDEX, VectorIndex.hnsw())); } } diff --git a/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java b/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java index 4af13ac7a..1f69f910d 100644 --- a/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java +++ b/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java @@ -57,7 +57,7 @@ public Builder vectors(String name, Float[][] vector) { } public Builder vectors(Consumer named) { - this.vectors = new Vectors(named); + this.vectors = Vectors.with(named); return this; } diff --git a/src/main/java/io/weaviate/client6/v1/Vectors.java b/src/main/java/io/weaviate/client6/v1/Vectors.java index 4d781fa91..9a69a5fa9 100644 --- a/src/main/java/io/weaviate/client6/v1/Vectors.java +++ b/src/main/java/io/weaviate/client6/v1/Vectors.java @@ -73,9 +73,9 @@ private Vectors(Map vectors) { this.namedVectors = Collections.unmodifiableMap(vectors); } - public Vectors(Consumer options) { - var vectors = new NamedVectors(options); - this.namedVectors = vectors.namedVectors; + static Vectors with(Consumer named) { + var vectors = new NamedVectors(named); + return new Vectors(vectors.namedVectors); } /** diff --git a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java index 8645d12c9..5e91085c7 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java @@ -5,57 +5,46 @@ import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.function.Consumer; import com.google.gson.Gson; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; +import io.weaviate.client6.v1.collections.Vectors.NamedVectors; -@AllArgsConstructor(access = AccessLevel.PACKAGE) -public class CollectionDefinition { - public final String name; - public final List properties; - public final Map vectorConfig; +public record CollectionDefinition(String name, List properties, Vectors vectors) { - public CollectionDefinition(String name, Consumer options) { + public static CollectionDefinition with(String name, Consumer options) { var config = new Configuration(options); - - this.name = name; - this.properties = config.properties; - this.vectorConfig = config.vectorConfig; - } - - public interface VectorConfig { - Object vectorizer(); - - String indexType(); - - Object indexConfiguration(); + return new CollectionDefinition(name, config.properties, config.vectors); } // Tucked Builder for additional collection configuration. public static class Configuration { - public List properties; - public final Map vectorConfig; + public List properties = new ArrayList<>(); + public Vectors vectors; public Configuration properties(Property... properties) { this.properties = Arrays.asList(properties); return this; } - // By default, we configure a "none" vectorizer. - public Configuration vector(String name) { - this.vectorConfig.put(name, new NoneVectorIndex()); + public Configuration vector(VectorIndex vector) { + this.vectors = new Vectors(vector); + return this; + } + + public Configuration vector(String name, VectorIndex vector) { + this.vectors = new Vectors(name, vector); + return this; + } + + public Configuration vectors(Consumer named) { + this.vectors = Vectors.with(named); return this; } Configuration(Consumer options) { - this.properties = new ArrayList<>(); - this.vectorConfig = new HashMap<>(); options.accept(this); } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java index 205ff736b..8747efccc 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java @@ -1,15 +1,9 @@ package io.weaviate.client6.v1.collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; import com.google.gson.annotations.SerializedName; -import io.weaviate.client6.v1.collections.CollectionDefinition.VectorConfig; - public class CollectionDefinitionDTO { @SerializedName("class") String collection; @@ -18,28 +12,15 @@ public class CollectionDefinitionDTO { List properties; @SerializedName("vectorConfig") - Map vectorIndices; + Vectors vectors; public CollectionDefinitionDTO(CollectionDefinition colDef) { - this.collection = colDef.name; - this.properties = colDef.properties; - - this.vectorIndices = new HashMap<>(); - for (var entry : colDef.vectorConfig.entrySet()) { - var index = entry.getValue(); - this.vectorIndices.put(entry.getKey(), Map.of( - "vectorizer", index.vectorizer(), - "vectorIndexType", index.indexType(), - "vectorIndexConfig", index.indexConfiguration())); - } + this.collection = colDef.name(); + this.properties = colDef.properties(); + this.vectors = colDef.vectors(); } CollectionDefinition toCollectionDefinition() { - return new CollectionDefinition( - collection, - properties, - vectorIndices.entrySet().stream() - .collect(Collectors.toMap( - Entry::getKey, entry -> (VectorConfig) entry.getValue()))); + return new CollectionDefinition(collection, properties, vectors); } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java index bbe708286..cd09d2ecf 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -13,9 +13,15 @@ import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import io.weaviate.client6.Config; import io.weaviate.client6.v1.Collection; +import io.weaviate.client6.v1.collections.CollectionDefinition.Configuration; +import io.weaviate.client6.v1.collections.VectorIndex.IndexType; import lombok.AllArgsConstructor; @AllArgsConstructor @@ -24,10 +30,51 @@ public class Collections { private final Config config; // TODO: use singleton configured in one place - private static final Gson gson = new Gson(); + private static final Gson gson = new GsonBuilder() + .registerTypeAdapter(Vectors.class, new TypeAdapter() { + Gson gson = new Gson(); + + @Override + public void write(JsonWriter out, Vectors value) throws IOException { + var unnamed = value.getUnnamed(); + if (unnamed.isPresent()) { + var index = unnamed.get(); + out.name("vectorIndexType"); + gson.toJson(index.type(), IndexType.class, out); + out.name("vectorizer"); + gson.toJson(index.vectorizer(), Vectorizer.class, out); + out.name("vectorIndexConfig"); + gson.toJson(index.configuration(), Configuration.class, out); + return; + } + + gson.toJson(value.asMap(), Map.class, out); + } + + @Override + public Vectors read(JsonReader in) throws IOException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'read'"); + } + + }) + .registerTypeHierarchyAdapter(Vectorizer.class, new TypeAdapter() { + Gson gson = new Gson(); + + @Override + public void write(JsonWriter out, Vectorizer value) throws IOException { + gson.toJson(value, value.getClass(), out); + } + + @Override + public Vectorizer read(JsonReader in) throws IOException { + return Vectorizer.none(); + } + }) + .create(); public void create(String name, Consumer options) throws IOException { - var collection = new CollectionDefinition(name, options); + var collection = CollectionDefinition.with(name, options); try (CloseableHttpClient httpclient = HttpClients.createDefault()) { ClassicHttpRequest httpPost = ClassicRequestBuilder diff --git a/src/main/java/io/weaviate/client6/v1/collections/HNSW.java b/src/main/java/io/weaviate/client6/v1/collections/HNSW.java new file mode 100644 index 000000000..11494c11f --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/HNSW.java @@ -0,0 +1,33 @@ +package io.weaviate.client6.v1.collections; + +import java.util.function.Consumer; + +public final record HNSW(Distance distance, boolean skip) implements VectorIndex.Configuration { + public enum Distance { + COSINE; + } + + public static HNSW with(Consumer options) { + var opt = new Options(options); + return new HNSW(opt.distance, opt.skip); + } + + public static class Options { + public Distance distance; + public Boolean skip; + + public Options distance(Distance distance) { + this.distance = distance; + return this; + } + + public Options disableIndexation() { + this.skip = true; + return this; + } + + public Options(Consumer options) { + options.accept(this); + } + } +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/NoneVectorIndex.java b/src/main/java/io/weaviate/client6/v1/collections/NoneVectorIndex.java deleted file mode 100644 index 83518497a..000000000 --- a/src/main/java/io/weaviate/client6/v1/collections/NoneVectorIndex.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.weaviate.client6.v1.collections; - -import java.util.HashMap; -import java.util.Map; - -public record NoneVectorIndex(Object vectorizer, String indexType, Object indexConfiguration) - implements CollectionDefinition.VectorConfig { - - public NoneVectorIndex() { - this(Map.of("none", new Object()), "flat", new HashMap<>()); - } -} diff --git a/src/main/java/io/weaviate/client6/v1/collections/NoneVectorizer.java b/src/main/java/io/weaviate/client6/v1/collections/NoneVectorizer.java new file mode 100644 index 000000000..014ed7cca --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/NoneVectorizer.java @@ -0,0 +1,10 @@ +package io.weaviate.client6.v1.collections; + +import java.util.Map; + +import com.google.gson.annotations.SerializedName; + +public class NoneVectorizer extends Vectorizer { + @SerializedName("none") + private final Map _configuration = Map.of(); +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java b/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java new file mode 100644 index 000000000..d3629d834 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java @@ -0,0 +1,39 @@ +package io.weaviate.client6.v1.collections; + +import java.util.function.Consumer; + +import com.google.gson.annotations.SerializedName; + +public record VectorIndex( + @SerializedName("vectorIndexType") IndexType type, + @SerializedName("vectorizer") V vectorizer, + @SerializedName("vectorIndexConfig") Configuration configuration) { + + public enum IndexType { + @SerializedName("hnsw") + HNSW; + } + + public static sealed interface Configuration permits HNSW { + } + + private VectorIndex(IndexType type, V vectorizer) { + this(type, vectorizer, null); + } + + public static VectorIndex bare() { + return new VectorIndex<>(null, Vectorizer.none()); + } + + public static VectorIndex hnsw() { + return new VectorIndex<>(IndexType.HNSW, Vectorizer.none()); + } + + public static VectorIndex hnsw(V vectorizer) { + return new VectorIndex<>(IndexType.HNSW, vectorizer); + } + + public static VectorIndex hnsw(V vectorizer, Consumer options) { + return new VectorIndex<>(IndexType.HNSW, vectorizer, HNSW.with(options)); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/Vectorizer.java b/src/main/java/io/weaviate/client6/v1/collections/Vectorizer.java new file mode 100644 index 000000000..ad9c4260f --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/Vectorizer.java @@ -0,0 +1,8 @@ +package io.weaviate.client6.v1.collections; + +// This class is WIP, I haven't decided how to structure it yet. +public abstract class Vectorizer { + public static NoneVectorizer none() { + return new NoneVectorizer(); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/collections/Vectors.java b/src/main/java/io/weaviate/client6/v1/collections/Vectors.java new file mode 100644 index 000000000..e92484b91 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/collections/Vectors.java @@ -0,0 +1,74 @@ +package io.weaviate.client6.v1.collections; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; + +public class Vectors { + public static final String DEFAULT = "default"; + + private final VectorIndex unnamedVector; + private final Map> namedVectors; + + public static Vectors unnamed(VectorIndex vector) { + return new Vectors(vector); + } + + public static Vectors of(String name, VectorIndex vector) { + return new Vectors(name, vector); + } + + public static Vectors of(VectorIndex vector) { + return new Vectors(DEFAULT, vector); + } + + public static Vectors with(Consumer named) { + var vectors = new NamedVectors(named); + return new Vectors(vectors.namedVectors); + } + + public VectorIndex get(String name) { + return namedVectors.get(name); + } + + public Optional> getUnnamed() { + return Optional.ofNullable(unnamedVector); + } + + public VectorIndex getDefault() { + return namedVectors.get(DEFAULT); + } + + public Map asMap() { + return Map.copyOf(namedVectors); + } + + Vectors(VectorIndex vector) { + this.unnamedVector = vector; + this.namedVectors = Map.of(); + } + + Vectors(String name, VectorIndex vector) { + this.unnamedVector = null; + this.namedVectors = Map.of(name, vector); + } + + Vectors(Map> vectors) { + this.unnamedVector = null; + this.namedVectors = vectors; + } + + public static class NamedVectors { + private final Map> namedVectors = new HashMap<>(); + + public NamedVectors vector(String name, VectorIndex vector) { + this.namedVectors.put(name, vector); + return this; + } + + NamedVectors(Consumer options) { + options.accept(this); + } + } +} diff --git a/src/test/java/io/weaviate/client6/v1/collections/VectorsTesa.java b/src/test/java/io/weaviate/client6/v1/collections/VectorsTesa.java new file mode 100644 index 000000000..aaf4c99cc --- /dev/null +++ b/src/test/java/io/weaviate/client6/v1/collections/VectorsTesa.java @@ -0,0 +1,34 @@ +package io.weaviate.client6.v1.collections; + +import org.junit.Test; + +import com.google.gson.Gson; +import com.jparams.junit4.description.Name; + +public class VectorsTesa { + private static final Gson gson = new Gson(); + + public static Object[][] testCases() { + return new Object[][] { + { + "hnsw index with 'none' vectorizer", + """ + { + "default-index": { + "vectorizer": { + "none": {} + }, + "vectorIndexType": "hnsw", + "vectorIndexConfig": {}, + } + } + """, + }, + }; + } + + @Test + @Name("{0}") + public void testVectorIndex_toJson() { + } +} From a8a958126d4bcb974ea6b7636d35e3bf6e40aa9d Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Thu, 13 Mar 2025 16:42:14 +0100 Subject: [PATCH 09/17] refactor: closeHttpClient and gRPC's channel on WeaviateClient.close WeaviateClient implements Closeable, so users can manage the associated resources according to their application's lifecycle. --- .../io/weaviate/client6/v1/DataITest.java | 2 +- .../v1/query/NearVectorQueryITest.java | 1 - .../io/weaviate/containers/Container.java | 8 ---- .../java/io/weaviate/containers/Weaviate.java | 25 ++++++++++- .../io/weaviate/client6/WeaviateClient.java | 24 ++++++++--- .../io/weaviate/client6/v1/Collection.java | 8 ++-- .../client6/v1/collections/Collections.java | 41 ++++++++++--------- .../client6/v1/collections/Vectors.java | 2 + .../io/weaviate/client6/v1/data/Data.java | 32 +++++++-------- .../io/weaviate/client6/v1/query/Query.java | 28 +++---------- 10 files changed, 92 insertions(+), 79 deletions(-) diff --git a/src/it/java/io/weaviate/client6/v1/DataITest.java b/src/it/java/io/weaviate/client6/v1/DataITest.java index a27ee08b0..8b4bac4bd 100644 --- a/src/it/java/io/weaviate/client6/v1/DataITest.java +++ b/src/it/java/io/weaviate/client6/v1/DataITest.java @@ -59,7 +59,7 @@ public void testCreateGetDelete() throws IOException { private static void createTestCollection() throws IOException { client.collections.create(COLLECTION, col -> col - .properties(Property.text("username")) + .properties(Property.text("username"), Property.integer("age")) .vector(VECTOR_INDEX, VectorIndex.hnsw())); } } diff --git a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java index 0c65e8bd6..07377d751 100644 --- a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java +++ b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java @@ -66,7 +66,6 @@ private static Map createVectors(int n) throws IOException { Map.of(), metadata -> metadata .id(randomUUID()) - .vectors(Vectors.unnamed(vector)) .vectors(Vectors.of(VECTOR_INDEX, vector))); created.put(object.metadata.id, vector); diff --git a/src/it/java/io/weaviate/containers/Container.java b/src/it/java/io/weaviate/containers/Container.java index d74880981..dc4803c24 100644 --- a/src/it/java/io/weaviate/containers/Container.java +++ b/src/it/java/io/weaviate/containers/Container.java @@ -38,38 +38,30 @@ private Group(Weaviate weaviate, GenericContainer... containers) { this.weaviate = weaviate; this.containers = Arrays.asList(containers); setSharedNetwork(); - System.out.println("Group initialized"); } public WeaviateClient getClient() { - System.out.println("get Weaviate client"); return weaviate.getClient(); } @Override public void start() { - System.out.println("Starting containers..."); containers.forEach(GenericContainer::start); - System.out.println("Starting Weaviate..."); weaviate.start(); - System.out.println("Started"); } @Override public void stop() { - System.out.println("Stopping..."); weaviate.stop(); containers.forEach(GenericContainer::stop); } private void setSharedNetwork() { - System.out.println("Set shared network..."); weaviate.setNetwork(Network.SHARED); containers.forEach(c -> c.setNetwork(Network.SHARED)); } public TestRule asTestRule() { - System.out.println("As TestRule!"); return new PerTestSuite(this); }; } diff --git a/src/it/java/io/weaviate/containers/Weaviate.java b/src/it/java/io/weaviate/containers/Weaviate.java index 71cf83de3..a2027762d 100644 --- a/src/it/java/io/weaviate/containers/Weaviate.java +++ b/src/it/java/io/weaviate/containers/Weaviate.java @@ -1,5 +1,6 @@ package io.weaviate.containers; +import java.io.IOException; import java.util.HashSet; import java.util.Set; @@ -9,12 +10,18 @@ import io.weaviate.client6.WeaviateClient; public class Weaviate extends WeaviateContainer { + private static WeaviateClient CLIENT; + public static final String VERSION = "1.29.0"; public static final String DOCKER_IMAGE = "semitechnologies/weaviate"; public WeaviateClient getClient() { - var config = new Config("http", getHttpHostAddress(), getGrpcHostAddress()); - return new WeaviateClient(config); + if (CLIENT == null) { + var config = new Config("http", getHttpHostAddress(), getGrpcHostAddress()); + CLIENT = new WeaviateClient(config); + System.out.println("create Weaviate client -- ONCE"); + } + return CLIENT; } public static Weaviate createDefault() { @@ -88,4 +95,18 @@ public Weaviate build() { private Weaviate(String dockerImageName) { super(dockerImageName); } + + @Override + public void stop() { + // Note: at the moment containers which are not created as a @TestRule + // will not be "stopped", so client's resources are also not being freed. + // This is fine in tests, but may produce warnings about the gRPC channel + // not shut down properly. + super.stop(); + try { + CLIENT.close(); + } catch (IOException e) { + // TODO: log error + } + } } diff --git a/src/main/java/io/weaviate/client6/WeaviateClient.java b/src/main/java/io/weaviate/client6/WeaviateClient.java index 6a0cec9d7..8dba725a5 100644 --- a/src/main/java/io/weaviate/client6/WeaviateClient.java +++ b/src/main/java/io/weaviate/client6/WeaviateClient.java @@ -1,15 +1,27 @@ package io.weaviate.client6; +import java.io.Closeable; +import java.io.IOException; + +import io.weaviate.client6.internal.GrpcClient; +import io.weaviate.client6.internal.HttpClient; import io.weaviate.client6.v1.collections.Collections; -public class WeaviateClient { - public final Collections collections; +public class WeaviateClient implements Closeable { + private final HttpClient http; + private final GrpcClient grpc; - // TODO: hide befind an internal HttpClient - private final Config config; + public final Collections collections; public WeaviateClient(Config config) { - this.config = config; - this.collections = new Collections(config); + this.http = new HttpClient(); + this.grpc = new GrpcClient(config); + this.collections = new Collections(config, http, grpc); + } + + @Override + public void close() throws IOException { + this.http.close(); + this.grpc.close(); } } diff --git a/src/main/java/io/weaviate/client6/v1/Collection.java b/src/main/java/io/weaviate/client6/v1/Collection.java index 095c0d4ba..e12f56915 100644 --- a/src/main/java/io/weaviate/client6/v1/Collection.java +++ b/src/main/java/io/weaviate/client6/v1/Collection.java @@ -1,6 +1,8 @@ package io.weaviate.client6.v1; import io.weaviate.client6.Config; +import io.weaviate.client6.internal.GrpcClient; +import io.weaviate.client6.internal.HttpClient; import io.weaviate.client6.v1.data.Data; import io.weaviate.client6.v1.query.Query; @@ -8,8 +10,8 @@ public class Collection { public final Query query; public final Data data; - public Collection(Config config, String collectionName) { - this.query = new Query<>(collectionName, config); - this.data = new Data<>(collectionName, config); + public Collection(String collectionName, Config config, GrpcClient grpc, HttpClient http) { + this.query = new Query<>(collectionName, grpc); + this.data = new Data<>(collectionName, config, http); } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java index cd09d2ecf..9696e38cb 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -4,8 +4,6 @@ import java.util.Map; import java.util.function.Consumer; -import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpStatus; @@ -19,6 +17,8 @@ import com.google.gson.stream.JsonWriter; import io.weaviate.client6.Config; +import io.weaviate.client6.internal.GrpcClient; +import io.weaviate.client6.internal.HttpClient; import io.weaviate.client6.v1.Collection; import io.weaviate.client6.v1.collections.CollectionDefinition.Configuration; import io.weaviate.client6.v1.collections.VectorIndex.IndexType; @@ -29,8 +29,12 @@ public class Collections { // TODO: hide befind an internal HttpClient private final Config config; + private final HttpClient httpClient; + private final GrpcClient grpcClient; + // TODO: use singleton configured in one place private static final Gson gson = new GsonBuilder() + // TODO: create TypeAdapters via TypeAdapterFactory .registerTypeAdapter(Vectors.class, new TypeAdapter() { Gson gson = new Gson(); @@ -76,24 +80,23 @@ public Vectorizer read(JsonReader in) throws IOException { public void create(String name, Consumer options) throws IOException { var collection = CollectionDefinition.with(name, options); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { - ClassicHttpRequest httpPost = ClassicRequestBuilder - .post(config.baseUrl() + "/schema") - .setEntity(collection.toJson(gson), ContentType.APPLICATION_JSON) - .build(); - - httpclient.execute(httpPost, response -> { - var entity = response.getEntity(); - if (response.getCode() != HttpStatus.SC_SUCCESS) { // Does not return 201 - var message = EntityUtils.toString(entity); - throw new RuntimeException("HTTP " + response.getCode() + ": " + message); - } - return null; - }); - } + ClassicHttpRequest httpPost = ClassicRequestBuilder + .post(config.baseUrl() + "/schema") + .setEntity(collection.toJson(gson), ContentType.APPLICATION_JSON) + .build(); + + // TODO: do not expose Apache HttpClient directly + httpClient.http.execute(httpPost, response -> { + var entity = response.getEntity(); + if (response.getCode() != HttpStatus.SC_SUCCESS) { // Does not return 201 + var message = EntityUtils.toString(entity); + throw new RuntimeException("HTTP " + response.getCode() + ": " + message); + } + return null; + }); } - public io.weaviate.client6.v1.Collection> use(String name) { - return new Collection<>(config, name); + public Collection> use(String name) { + return new Collection<>(name, config, grpcClient, httpClient); } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/Vectors.java b/src/main/java/io/weaviate/client6/v1/collections/Vectors.java index e92484b91..1c6e0a481 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Vectors.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Vectors.java @@ -40,6 +40,8 @@ public VectorIndex getDefault() { return namedVectors.get(DEFAULT); } + // This needs to document the fact that this only returns named vectors. + // Rename to "getNamedVectors()" public Map asMap() { return Map.copyOf(namedVectors); } diff --git a/src/main/java/io/weaviate/client6/v1/data/Data.java b/src/main/java/io/weaviate/client6/v1/data/Data.java index 03e6dfad8..bad142b26 100644 --- a/src/main/java/io/weaviate/client6/v1/data/Data.java +++ b/src/main/java/io/weaviate/client6/v1/data/Data.java @@ -15,6 +15,7 @@ import com.google.gson.Gson; import io.weaviate.client6.Config; +import io.weaviate.client6.internal.HttpClient; import io.weaviate.client6.v1.ObjectMetadata; import lombok.AllArgsConstructor; @@ -28,25 +29,24 @@ public class Data { // TODO: hide befind an internal HttpClient private final Config config; + private final HttpClient httpClient; public WeaviateObject insert(T object, Consumer options) throws IOException { var body = new WeaviateObject<>(collectionName, object, options); - try (CloseableHttpClient httpclient = HttpClients.createDefault()) { - ClassicHttpRequest httpPost = ClassicRequestBuilder - .post(config.baseUrl() + "/objects") - .setEntity(body.toJson(gson), ContentType.APPLICATION_JSON) - .build(); + ClassicHttpRequest httpPost = ClassicRequestBuilder + .post(config.baseUrl() + "/objects") + .setEntity(body.toJson(gson), ContentType.APPLICATION_JSON) + .build(); - return httpclient.execute(httpPost, response -> { - var entity = response.getEntity(); - if (response.getCode() != HttpStatus.SC_SUCCESS) { // Does not return 201 - var message = EntityUtils.toString(entity); - throw new RuntimeException("HTTP " + response.getCode() + ": " + message); - } + return httpClient.http.execute(httpPost, response -> { + var entity = response.getEntity(); + if (response.getCode() != HttpStatus.SC_SUCCESS) { // Does not return 201 + var message = EntityUtils.toString(entity); + throw new RuntimeException("HTTP " + response.getCode() + ": " + message); + } - return WeaviateObject.fromJson(gson, entity.getContent()); - }); - } + return WeaviateObject.fromJson(gson, entity.getContent()); + }); } public Optional> get(String id) throws IOException { @@ -55,7 +55,7 @@ public Optional> get(String id) throws IOException { .get(config.baseUrl() + "/objects/" + collectionName + "/" + id + "?include=vector") .build(); - return httpclient.execute(httpGet, response -> { + return httpClient.http.execute(httpGet, response -> { if (response.getCode() == HttpStatus.SC_NOT_FOUND) { return Optional.empty(); } @@ -73,7 +73,7 @@ public void delete(String id) throws IOException { .delete(config.baseUrl() + "/objects/" + collectionName + "/" + id) .build(); - httpclient.execute(httpGet, response -> { + httpClient.http.execute(httpGet, response -> { if (response.getCode() != HttpStatus.SC_NO_CONTENT) { throw new RuntimeException(EntityUtils.toString(response.getEntity())); } diff --git a/src/main/java/io/weaviate/client6/v1/query/Query.java b/src/main/java/io/weaviate/client6/v1/query/Query.java index 1aec5bf59..7ac1508c7 100644 --- a/src/main/java/io/weaviate/client6/v1/query/Query.java +++ b/src/main/java/io/weaviate/client6/v1/query/Query.java @@ -9,17 +9,12 @@ import com.google.gson.Gson; -import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; -import io.grpc.stub.MetadataUtils; -import io.weaviate.client6.Config; -import io.weaviate.client6.grpc.protocol.v1.WeaviateGrpc; -import io.weaviate.client6.grpc.protocol.v1.WeaviateGrpc.WeaviateBlockingStub; import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoProperties.Value; import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.MetadataResult; import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.SearchReply; import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.SearchRequest; import io.weaviate.client6.internal.GRPC; +import io.weaviate.client6.internal.GrpcClient; public class Query { // TODO: inject singleton as dependency @@ -28,20 +23,13 @@ public class Query { // TODO: this should be wrapped around in some TypeInspector etc. private final String collectionName; - // TODO: hide befind an internal HttpClient - private final Config config; - // TODO: implement Closeable and call grpc.shutdown() on exit // (probably on a "higher" level); - private WeaviateBlockingStub grpc; + private final GrpcClient grpcClient; - public Query(String collectionName, Config config) { - this.config = config; + public Query(String collectionName, GrpcClient grpc) { + this.grpcClient = grpc; this.collectionName = collectionName; - - // TODO: add request headers (config.headers + authorization) - this.grpc = WeaviateGrpc.newBlockingStub(buildChannel(config)) - .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(new io.grpc.Metadata())); } public QueryResult nearVector(Float[] vector, Consumer options) { @@ -60,7 +48,7 @@ public QueryResult nearVector(Float[] vector, Consumer op } private QueryResult search(SearchRequest req) { - var reply = grpc.search(req); + var reply = grpcClient.grpc.search(req); return deserializeUntyped(reply); } @@ -113,10 +101,4 @@ private static Object convertProtoValue(Value value) { } return null; } - - private static ManagedChannel buildChannel(Config config) { - ManagedChannelBuilder channelBuilder = ManagedChannelBuilder.forTarget(config.grpcAddress()); - channelBuilder.usePlaintext(); - return channelBuilder.build(); - } } From 6158af56bc2b17809be7870f7747bc9ea04377c0 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Thu, 13 Mar 2025 19:05:33 +0100 Subject: [PATCH 10/17] feat: supply query parameters to GET /objects/ --- .../io/weaviate/client6/v1/DataITest.java | 2 +- .../weaviate/client6/internal/GrpcClient.java | 37 +++++++++ .../weaviate/client6/internal/HttpClient.java | 20 +++++ .../client6/v1/data/ConsistencyLevel.java | 5 ++ .../io/weaviate/client6/v1/data/Data.java | 7 +- .../client6/v1/data/GetParameters.java | 76 +++++++++++++++++++ .../client6/v1/data/QueryParameters.java | 40 ++++++++++ .../client6/v1/collections/VectorsTesa.java | 34 --------- .../client6/v1/data/QueryParametersTest.java | 48 ++++++++++++ 9 files changed, 233 insertions(+), 36 deletions(-) create mode 100644 src/main/java/io/weaviate/client6/internal/GrpcClient.java create mode 100644 src/main/java/io/weaviate/client6/internal/HttpClient.java create mode 100644 src/main/java/io/weaviate/client6/v1/data/ConsistencyLevel.java create mode 100644 src/main/java/io/weaviate/client6/v1/data/GetParameters.java create mode 100644 src/main/java/io/weaviate/client6/v1/data/QueryParameters.java delete mode 100644 src/test/java/io/weaviate/client6/v1/collections/VectorsTesa.java create mode 100644 src/test/java/io/weaviate/client6/v1/data/QueryParametersTest.java diff --git a/src/it/java/io/weaviate/client6/v1/DataITest.java b/src/it/java/io/weaviate/client6/v1/DataITest.java index 8b4bac4bd..ff6ee333a 100644 --- a/src/it/java/io/weaviate/client6/v1/DataITest.java +++ b/src/it/java/io/weaviate/client6/v1/DataITest.java @@ -35,7 +35,7 @@ public void testCreateGetDelete() throws IOException { .id(id) .vectors(Vectors.of(VECTOR_INDEX, vector))); - var object = things.data.get(id); + var object = things.data.get(id, query -> query.withVector()); Assertions.assertThat(object) .as("object exists after insert").get() .satisfies(obj -> { diff --git a/src/main/java/io/weaviate/client6/internal/GrpcClient.java b/src/main/java/io/weaviate/client6/internal/GrpcClient.java new file mode 100644 index 000000000..3e4045cc6 --- /dev/null +++ b/src/main/java/io/weaviate/client6/internal/GrpcClient.java @@ -0,0 +1,37 @@ +package io.weaviate.client6.internal; + +import java.io.Closeable; +import java.io.IOException; + +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.stub.MetadataUtils; +import io.weaviate.client6.Config; +import io.weaviate.client6.grpc.protocol.v1.WeaviateGrpc; +import io.weaviate.client6.grpc.protocol.v1.WeaviateGrpc.WeaviateBlockingStub; + +public class GrpcClient implements Closeable { + private final ManagedChannel channel; + public final WeaviateBlockingStub grpc; + + public GrpcClient(Config config) { + this.channel = buildChannel(config); + this.grpc = buildStub(channel); + } + + @Override + public void close() throws IOException { + channel.shutdown(); + } + + private static ManagedChannel buildChannel(Config config) { + ManagedChannelBuilder channelBuilder = ManagedChannelBuilder.forTarget(config.grpcAddress()); + channelBuilder.usePlaintext(); + return channelBuilder.build(); + } + + private static WeaviateBlockingStub buildStub(ManagedChannel channel) { + return WeaviateGrpc.newBlockingStub(channel) + .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(new io.grpc.Metadata())); + } +} diff --git a/src/main/java/io/weaviate/client6/internal/HttpClient.java b/src/main/java/io/weaviate/client6/internal/HttpClient.java new file mode 100644 index 000000000..624e4a9ca --- /dev/null +++ b/src/main/java/io/weaviate/client6/internal/HttpClient.java @@ -0,0 +1,20 @@ +package io.weaviate.client6.internal; + +import java.io.Closeable; +import java.io.IOException; + +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; + +public class HttpClient implements Closeable { + public final CloseableHttpClient http; + + public HttpClient() { + http = HttpClients.createDefault(); + } + + @Override + public void close() throws IOException { + http.close(); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/data/ConsistencyLevel.java b/src/main/java/io/weaviate/client6/v1/data/ConsistencyLevel.java new file mode 100644 index 000000000..1011a7a02 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/ConsistencyLevel.java @@ -0,0 +1,5 @@ +package io.weaviate.client6.v1.data; + +public enum ConsistencyLevel { + ONE, QUORUM, ALL +} diff --git a/src/main/java/io/weaviate/client6/v1/data/Data.java b/src/main/java/io/weaviate/client6/v1/data/Data.java index bad142b26..e5f457b23 100644 --- a/src/main/java/io/weaviate/client6/v1/data/Data.java +++ b/src/main/java/io/weaviate/client6/v1/data/Data.java @@ -50,9 +50,14 @@ public WeaviateObject insert(T object, Consumer optio } public Optional> get(String id) throws IOException { + return get(id, q -> { + }); + } + + public Optional> get(String id, Consumer query) throws IOException { try (CloseableHttpClient httpclient = HttpClients.createDefault()) { ClassicHttpRequest httpGet = ClassicRequestBuilder - .get(config.baseUrl() + "/objects/" + collectionName + "/" + id + "?include=vector") + .get(config.baseUrl() + "/objects/" + collectionName + "/" + id + QueryParameters.encodeGet(query)) .build(); return httpClient.http.execute(httpGet, response -> { diff --git a/src/main/java/io/weaviate/client6/v1/data/GetParameters.java b/src/main/java/io/weaviate/client6/v1/data/GetParameters.java new file mode 100644 index 000000000..819ffc09c --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/GetParameters.java @@ -0,0 +1,76 @@ +package io.weaviate.client6.v1.data; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +public class GetParameters implements QueryParameters { + private enum Include { + VECTOR, CLASSIFICATION, INTERPRETATION; + + String toLowerCase() { + return this.name().toLowerCase(); + } + } + + private Set include = new LinkedHashSet<>(); // Preserves insertion order, helps testing + private ConsistencyLevel consistency; + private String nodeName; + private String tenant; + + GetParameters(Consumer options) { + options.accept(this); + } + + public GetParameters withVector() { + include.add(Include.VECTOR); + return this; + } + + public GetParameters withClassification() { + include.add(Include.CLASSIFICATION); + return this; + } + + public GetParameters withInterpretation() { + include.add(Include.INTERPRETATION); + return this; + } + + public GetParameters consistencyLevel(ConsistencyLevel consistency) { + this.consistency = consistency; + return this; + } + + public GetParameters nodeName(String name) { + this.nodeName = name; + return this; + } + + public GetParameters tenant(String name) { + this.tenant = name; + return this; + } + + @Override + public String encode() { + var sb = new StringBuilder(); + + if (!include.isEmpty()) { + List includeString = include.stream().map(Include::toLowerCase).toList(); + QueryParameters.addRaw(sb, "include", String.join(",", includeString)); + } + + if (consistency != null) { + QueryParameters.add(sb, "consistency_level", consistency.name()); + } + if (nodeName != null) { + QueryParameters.add(sb, "node_name", nodeName); + } + if (tenant != null) { + QueryParameters.add(sb, "tenant", tenant); + } + return sb.toString(); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/data/QueryParameters.java b/src/main/java/io/weaviate/client6/v1/data/QueryParameters.java new file mode 100644 index 000000000..2b94c834c --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/data/QueryParameters.java @@ -0,0 +1,40 @@ +package io.weaviate.client6.v1.data; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; + +interface QueryParameters { + /* Implementations must return an empty string if there're no parameters. */ + String encode(); + + static String encodeGet(Consumer options) { + return with(new GetParameters(options)); + } + + private static

String with(P parameters) { + var encoded = parameters.encode(); + return encoded.isEmpty() ? "" : "?" + encoded; + } + + static void add(StringBuilder sb, String key, String value) { + addRaw(sb, encode(key), encode(value)); + } + + static void addRaw(StringBuilder sb, String key, String value) { + if (!sb.isEmpty()) { + sb.append("&"); + } + sb.append(key).append("=").append(value); + } + + static String encode(String value) { + try { + return URLEncoder.encode(value, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // Will never happen, as we are using standard encoding. + return value; + } + } +} diff --git a/src/test/java/io/weaviate/client6/v1/collections/VectorsTesa.java b/src/test/java/io/weaviate/client6/v1/collections/VectorsTesa.java deleted file mode 100644 index aaf4c99cc..000000000 --- a/src/test/java/io/weaviate/client6/v1/collections/VectorsTesa.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.weaviate.client6.v1.collections; - -import org.junit.Test; - -import com.google.gson.Gson; -import com.jparams.junit4.description.Name; - -public class VectorsTesa { - private static final Gson gson = new Gson(); - - public static Object[][] testCases() { - return new Object[][] { - { - "hnsw index with 'none' vectorizer", - """ - { - "default-index": { - "vectorizer": { - "none": {} - }, - "vectorIndexType": "hnsw", - "vectorIndexConfig": {}, - } - } - """, - }, - }; - } - - @Test - @Name("{0}") - public void testVectorIndex_toJson() { - } -} diff --git a/src/test/java/io/weaviate/client6/v1/data/QueryParametersTest.java b/src/test/java/io/weaviate/client6/v1/data/QueryParametersTest.java new file mode 100644 index 000000000..6c6caa676 --- /dev/null +++ b/src/test/java/io/weaviate/client6/v1/data/QueryParametersTest.java @@ -0,0 +1,48 @@ +package io.weaviate.client6.v1.data; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.jparams.junit4.JParamsTestRunner; +import com.jparams.junit4.data.DataMethod; + +@RunWith(JParamsTestRunner.class) +public class QueryParametersTest { + + public static Object[][] testCases() { + return new Object[][] { + { + QueryParameters.encodeGet(q -> q + .withVector() + .nodeName("node-1")), + "?include=vector&node_name=node-1", + }, + { + QueryParameters.encodeGet(q -> q + .withVector() + .withClassification() + .tenant("JohnDoe")), + "?include=vector,classification&tenant=JohnDoe", + }, + { + QueryParameters.encodeGet(q -> q + .consistencyLevel(ConsistencyLevel.ALL) + .nodeName("node-1") + .tenant("JohnDoe")), + "?consistency_level=ALL&node_name=node-1&tenant=JohnDoe", + }, + { + QueryParameters.encodeGet(q -> { + }), + "", + }, + }; + } + + @Test + @DataMethod(source = QueryParametersTest.class, method = "testCases") + public void testEncode(String got, String want) { + Assertions.assertThat(got).isEqualTo(want).as("expected query parameters"); + } +} From 17f246cd85516e727c18c219ff63f7e24d819015 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Thu, 13 Mar 2025 19:37:14 +0100 Subject: [PATCH 11/17] refactor: use records for WeaviateObject and ObjectMetadata --- .../io/weaviate/client6/v1/DataITest.java | 6 ++-- .../v1/query/NearVectorQueryITest.java | 2 +- .../weaviate/client6/v1/ObjectMetadata.java | 19 ++----------- .../client6/v1/data/WeaviateObject.java | 13 ++------- .../client6/v1/data/WeaviateObjectDTO.java | 10 +++---- .../client6/v1/ObjectMetadataTest.java | 28 +++++++++---------- 6 files changed, 28 insertions(+), 50 deletions(-) diff --git a/src/it/java/io/weaviate/client6/v1/DataITest.java b/src/it/java/io/weaviate/client6/v1/DataITest.java index ff6ee333a..aee8e3d39 100644 --- a/src/it/java/io/weaviate/client6/v1/DataITest.java +++ b/src/it/java/io/weaviate/client6/v1/DataITest.java @@ -39,14 +39,14 @@ public void testCreateGetDelete() throws IOException { Assertions.assertThat(object) .as("object exists after insert").get() .satisfies(obj -> { - Assertions.assertThat(obj.metadata.id) + Assertions.assertThat(obj.metadata().id()) .as("object id").isEqualTo(id); - Assertions.assertThat(obj.metadata.vectors).extracting(Vectors::getSingle) + Assertions.assertThat(obj.metadata().vectors()).extracting(Vectors::getSingle) .asInstanceOf(InstanceOfAssertFactories.OPTIONAL).as("has single vector").get() .asInstanceOf(InstanceOfAssertFactories.array(Float[].class)).containsExactly(vector); - Assertions.assertThat(obj.properties) + Assertions.assertThat(obj.properties()) .as("has expected properties") .containsEntry("username", "john doe"); }); diff --git a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java index 07377d751..8c5cb8b1b 100644 --- a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java +++ b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java @@ -68,7 +68,7 @@ private static Map createVectors(int n) throws IOException { .id(randomUUID()) .vectors(Vectors.of(VECTOR_INDEX, vector))); - created.put(object.metadata.id, vector); + created.put(object.metadata().id(), vector); } return created; diff --git a/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java b/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java index 1f69f910d..d63b0225a 100644 --- a/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java +++ b/src/main/java/io/weaviate/client6/v1/ObjectMetadata.java @@ -2,24 +2,11 @@ import java.util.function.Consumer; -public class ObjectMetadata { - public final String id; - public final Vectors vectors; +public record ObjectMetadata(String id, Vectors vectors) { - // ObjectMetadata(String id, Vectors vectors) { - // this(m -> m.id(id).vectors(vectors)); - // } - - public ObjectMetadata(String id, Vectors vectors) { - this.id = id; - this.vectors = vectors; - } - - public ObjectMetadata(Consumer options) { + public static ObjectMetadata with(Consumer options) { var opt = new Builder(options); - - this.id = opt.id; - this.vectors = opt.vectors; + return new ObjectMetadata(opt.id, opt.vectors); } public static class Builder { diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java index 8aa68d107..edc08d251 100644 --- a/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObject.java @@ -9,22 +9,13 @@ import com.google.gson.Gson; import io.weaviate.client6.v1.ObjectMetadata; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; // TODO: unify this with collections.SearchObject -@AllArgsConstructor(access = AccessLevel.PACKAGE) -public class WeaviateObject { - public final String collection; - public final T properties; - public final ObjectMetadata metadata; +public record WeaviateObject(String collection, T properties, ObjectMetadata metadata) { WeaviateObject(String collection, T properties, Consumer options) { - - this.collection = collection; - this.properties = properties; - this.metadata = new ObjectMetadata(options); + this(collection, properties, ObjectMetadata.with(options)); } // JSON serialization ---------------- diff --git a/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java b/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java index 3767a7abc..9d3e3fcc7 100644 --- a/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java +++ b/src/main/java/io/weaviate/client6/v1/data/WeaviateObjectDTO.java @@ -20,12 +20,12 @@ class WeaviateObjectDTO { Map vectors; WeaviateObjectDTO(WeaviateObject object) { - this.collection = object.collection; - this.properties = object.properties; + this.collection = object.collection(); + this.properties = object.properties(); - if (object.metadata != null) { - this.id = object.metadata.id; - this.vectors = object.metadata.vectors.asMap(); + if (object.metadata() != null) { + this.id = object.metadata().id(); + this.vectors = object.metadata().vectors().asMap(); } } diff --git a/src/test/java/io/weaviate/client6/v1/ObjectMetadataTest.java b/src/test/java/io/weaviate/client6/v1/ObjectMetadataTest.java index 549ae24a9..542f5f7da 100644 --- a/src/test/java/io/weaviate/client6/v1/ObjectMetadataTest.java +++ b/src/test/java/io/weaviate/client6/v1/ObjectMetadataTest.java @@ -9,17 +9,17 @@ public class ObjectMetadataTest { @Test public final void testMetadata_id() { - var metadata = new ObjectMetadata(m -> m.id("object-1")); - Assertions.assertThat(metadata.id) + var metadata = ObjectMetadata.with(m -> m.id("object-1")); + Assertions.assertThat(metadata.id()) .as("object id").isEqualTo("object-1"); } @Test public final void testVectorsMetadata_unnamed() { Float[] vector = { 1f, 2f, 3f }; - var metadata = new ObjectMetadata(m -> m.vectors(Vectors.unnamed(vector))); + var metadata = ObjectMetadata.with(m -> m.vectors(Vectors.unnamed(vector))); - Assertions.assertThat(metadata.vectors) + Assertions.assertThat(metadata.vectors()) .as("unnamed vector").isNotNull() .returns(Optional.of(vector), Vectors::getUnnamed) .returns(Optional.empty(), Vectors::getSingle); @@ -28,9 +28,9 @@ public final void testVectorsMetadata_unnamed() { @Test public final void testVectorsMetadata_default() { Float[] vector = { 1f, 2f, 3f }; - var metadata = new ObjectMetadata(m -> m.vectors(vector)); + var metadata = ObjectMetadata.with(m -> m.vectors(vector)); - Assertions.assertThat(metadata.vectors) + Assertions.assertThat(metadata.vectors()) .as("default vector").isNotNull() .returns(vector, Vectors::getDefaultSingle) .returns(Optional.of(vector), Vectors::getSingle) @@ -40,9 +40,9 @@ public final void testVectorsMetadata_default() { @Test public final void testVectorsMetadata_default_2d() { Float[][] vector = { { 1f, 2f, 3f }, { 1f, 2f, 3f } }; - var metadata = new ObjectMetadata(m -> m.vectors(vector)); + var metadata = ObjectMetadata.with(m -> m.vectors(vector)); - Assertions.assertThat(metadata.vectors) + Assertions.assertThat(metadata.vectors()) .as("default 2d vector").isNotNull() .returns(vector, Vectors::getDefaultMulti) .returns(Optional.of(vector), Vectors::getMulti) @@ -52,9 +52,9 @@ public final void testVectorsMetadata_default_2d() { @Test public final void testVectorsMetadata_named() { Float[] vector = { 1f, 2f, 3f }; - var metadata = new ObjectMetadata(m -> m.vectors("vector-1", vector)); + var metadata = ObjectMetadata.with(m -> m.vectors("vector-1", vector)); - Assertions.assertThat(metadata.vectors) + Assertions.assertThat(metadata.vectors()) .as("named vector").isNotNull() .returns(vector, v -> v.getSingle("vector-1")) .returns(Optional.of(vector), Vectors::getSingle) @@ -64,9 +64,9 @@ public final void testVectorsMetadata_named() { @Test public final void testVectorsMetadata_named_2d() { Float[][] vector = { { 1f, 2f, 3f }, { 1f, 2f, 3f } }; - var metadata = new ObjectMetadata(m -> m.vectors("vector-1", vector)); + var metadata = ObjectMetadata.with(m -> m.vectors("vector-1", vector)); - Assertions.assertThat(metadata.vectors) + Assertions.assertThat(metadata.vectors()) .as("named 2d vector").isNotNull() .returns(vector, v -> v.getMulti("vector-1")) .returns(Optional.of(vector), Vectors::getMulti) @@ -77,12 +77,12 @@ public final void testVectorsMetadata_named_2d() { public final void testVectorsMetadata_multiple_named() { Float[][] vector_1 = { { 1f, 2f, 3f }, { 1f, 2f, 3f } }; Float[] vector_2 = { 4f, 5f, 6f }; - var metadata = new ObjectMetadata(m -> m.vectors( + var metadata = ObjectMetadata.with(m -> m.vectors( named -> named .vector("vector-1", vector_1) .vector("vector-2", vector_2))); - Assertions.assertThat(metadata.vectors) + Assertions.assertThat(metadata.vectors()) .as("multiple named vectors").isNotNull() .returns(vector_1, v -> v.getMulti("vector-1")) .returns(vector_2, v -> v.getSingle("vector-2")) From febdb12d98e50e195761fc2dcb4c17af1f89837e Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Fri, 14 Mar 2025 11:32:28 +0100 Subject: [PATCH 12/17] ci: stop shared test containers and close gRPC/http clients after test run --- pom.xml | 8 +++++++- .../java/io/weaviate/containers/Container.java | 18 ++++++++++++++++++ .../io/weaviate/containers/TestListener.java | 14 ++++++++++++++ .../java/io/weaviate/containers/Weaviate.java | 16 ++++++++++------ 4 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 src/it/java/io/weaviate/containers/TestListener.java diff --git a/pom.xml b/pom.xml index 98283db29..0adc6823e 100644 --- a/pom.xml +++ b/pom.xml @@ -264,7 +264,13 @@ @see: https://www.oracle.com/corporate/features/understanding-java-9-modules.html --> --add-opens=java.base/java.lang=ALL-UNNAMED - + + + + listener + io.weaviate.containers.TestListener + + diff --git a/src/it/java/io/weaviate/containers/Container.java b/src/it/java/io/weaviate/containers/Container.java index dc4803c24..471f996ff 100644 --- a/src/it/java/io/weaviate/containers/Container.java +++ b/src/it/java/io/weaviate/containers/Container.java @@ -18,9 +18,27 @@ public class Container { public static final Contextionary CONTEXTIONARY = Contextionary.createDefault(); static { + startAll(); + } + + /** Start all shared Testcontainers. */ + // TODO: start lazily? + static void startAll() { WEAVIATE.start(); } + /** + * Stop all shared Testcontainers created in {@link #startAll}. + *

+ * Testcontainer's Ryuk will reap any dangling containers after the tests + * finish. However, since {@link Weaviate} instances also hold a + * {@link WeaviateClient}, we want to stop them proactively to + * close client connections. + */ + static void stopAll() { + WEAVIATE.stop(); + } + public static Group compose(Weaviate weaviate, GenericContainer... containers) { return new Group(weaviate, containers); } diff --git a/src/it/java/io/weaviate/containers/TestListener.java b/src/it/java/io/weaviate/containers/TestListener.java new file mode 100644 index 000000000..72889125b --- /dev/null +++ b/src/it/java/io/weaviate/containers/TestListener.java @@ -0,0 +1,14 @@ +package io.weaviate.containers; + +import org.junit.runner.Result; +import org.junit.runner.notification.RunListener; + +public class TestListener extends RunListener { + + @Override + public void testRunFinished(Result result) throws Exception { + Container.stopAll(); + super.testRunFinished(result); + } + +} diff --git a/src/it/java/io/weaviate/containers/Weaviate.java b/src/it/java/io/weaviate/containers/Weaviate.java index a2027762d..2ee9619b1 100644 --- a/src/it/java/io/weaviate/containers/Weaviate.java +++ b/src/it/java/io/weaviate/containers/Weaviate.java @@ -10,18 +10,22 @@ import io.weaviate.client6.WeaviateClient; public class Weaviate extends WeaviateContainer { - private static WeaviateClient CLIENT; + private static WeaviateClient clientInstance; public static final String VERSION = "1.29.0"; public static final String DOCKER_IMAGE = "semitechnologies/weaviate"; + /** + * Get a client for the current Weaviate container. + * As we aren't running tests in parallel at the moment, + * this is not made thread-safe. + */ public WeaviateClient getClient() { - if (CLIENT == null) { + if (clientInstance == null) { var config = new Config("http", getHttpHostAddress(), getGrpcHostAddress()); - CLIENT = new WeaviateClient(config); - System.out.println("create Weaviate client -- ONCE"); + clientInstance = new WeaviateClient(config); } - return CLIENT; + return clientInstance; } public static Weaviate createDefault() { @@ -104,7 +108,7 @@ public void stop() { // not shut down properly. super.stop(); try { - CLIENT.close(); + clientInstance.close(); } catch (IOException e) { // TODO: log error } From a6f41cff0b674d9fe981262056624899c9472014 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sun, 16 Mar 2025 17:34:55 +0100 Subject: [PATCH 13/17] spike: manage DTOs via TypeAdapterFactory --- .../io/weaviate/client6/v1/DataITest.java | 4 +- .../v1/query/NearVectorQueryITest.java | 4 +- .../io/weaviate/containers/Container.java | 5 +- .../java/io/weaviate/containers/Weaviate.java | 7 + .../internal/DtoTypeAdapterFactory.java | 61 ++++++++ .../weaviate/client6/internal/HttpClient.java | 3 + .../v1/collections/CollectionDefinition.java | 17 --- .../collections/CollectionDefinitionDTO.java | 23 ++- .../client6/v1/collections/Collections.java | 16 +- .../weaviate/client6/v1/collections/HNSW.java | 14 +- .../client6/v1/collections/VectorIndex.java | 29 ++-- .../client6/v1/collections/VectorsTest.java | 144 ++++++++++++++++++ .../internal/DtoTypeAdapterFactoryTest.java | 45 ++++++ 13 files changed, 328 insertions(+), 44 deletions(-) create mode 100644 src/main/java/io/weaviate/client6/internal/DtoTypeAdapterFactory.java create mode 100644 src/test/java/io/weaviate/client6/v1/collections/VectorsTest.java create mode 100644 src/test/java/io/weaviate/internal/DtoTypeAdapterFactoryTest.java diff --git a/src/it/java/io/weaviate/client6/v1/DataITest.java b/src/it/java/io/weaviate/client6/v1/DataITest.java index aee8e3d39..f64702f1e 100644 --- a/src/it/java/io/weaviate/client6/v1/DataITest.java +++ b/src/it/java/io/weaviate/client6/v1/DataITest.java @@ -12,6 +12,8 @@ import io.weaviate.client6.WeaviateClient; import io.weaviate.client6.v1.collections.Property; import io.weaviate.client6.v1.collections.VectorIndex; +import io.weaviate.client6.v1.collections.VectorIndex.IndexingStrategy; +import io.weaviate.client6.v1.collections.Vectorizer; import io.weaviate.containers.Container; public class DataITest extends ConcurrentTest { @@ -60,6 +62,6 @@ private static void createTestCollection() throws IOException { client.collections.create(COLLECTION, col -> col .properties(Property.text("username"), Property.integer("age")) - .vector(VECTOR_INDEX, VectorIndex.hnsw())); + .vector(VECTOR_INDEX, new VectorIndex<>(IndexingStrategy.hnsw(), Vectorizer.none()))); } } diff --git a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java index 8c5cb8b1b..0b8693b75 100644 --- a/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java +++ b/src/it/java/io/weaviate/client6/v1/query/NearVectorQueryITest.java @@ -14,6 +14,8 @@ import io.weaviate.client6.WeaviateClient; import io.weaviate.client6.v1.Vectors; import io.weaviate.client6.v1.collections.VectorIndex; +import io.weaviate.client6.v1.collections.VectorIndex.IndexingStrategy; +import io.weaviate.client6.v1.collections.Vectorizer; import io.weaviate.containers.Container; public class NearVectorQueryITest extends ConcurrentTest { @@ -81,6 +83,6 @@ private static Map createVectors(int n) throws IOException { */ private static void createTestCollection() throws IOException { client.collections.create(COLLECTION, cfg -> cfg - .vector(VECTOR_INDEX, VectorIndex.hnsw())); + .vector(VECTOR_INDEX, new VectorIndex<>(IndexingStrategy.hnsw(), Vectorizer.none()))); } } diff --git a/src/it/java/io/weaviate/containers/Container.java b/src/it/java/io/weaviate/containers/Container.java index 471f996ff..9ef69cfe8 100644 --- a/src/it/java/io/weaviate/containers/Container.java +++ b/src/it/java/io/weaviate/containers/Container.java @@ -22,9 +22,9 @@ public class Container { } /** Start all shared Testcontainers. */ - // TODO: start lazily? + // TODO: start lazily! static void startAll() { - WEAVIATE.start(); + // WEAVIATE.start(); } /** @@ -44,7 +44,6 @@ public static Group compose(Weaviate weaviate, GenericContainer... containers } public static TestRule asTestRule(Startable container) { - System.out.print("HERE"); return new PerTestSuite(container); }; diff --git a/src/it/java/io/weaviate/containers/Weaviate.java b/src/it/java/io/weaviate/containers/Weaviate.java index 2ee9619b1..6126d5150 100644 --- a/src/it/java/io/weaviate/containers/Weaviate.java +++ b/src/it/java/io/weaviate/containers/Weaviate.java @@ -21,6 +21,10 @@ public class Weaviate extends WeaviateContainer { * this is not made thread-safe. */ public WeaviateClient getClient() { + // FIXME: control from containers? + if (!isRunning()) { + start(); + } if (clientInstance == null) { var config = new Config("http", getHttpHostAddress(), getGrpcHostAddress()); clientInstance = new WeaviateClient(config); @@ -107,6 +111,9 @@ public void stop() { // This is fine in tests, but may produce warnings about the gRPC channel // not shut down properly. super.stop(); + if (clientInstance == null) { + return; + } try { clientInstance.close(); } catch (IOException e) { diff --git a/src/main/java/io/weaviate/client6/internal/DtoTypeAdapterFactory.java b/src/main/java/io/weaviate/client6/internal/DtoTypeAdapterFactory.java new file mode 100644 index 000000000..7f88bbc9f --- /dev/null +++ b/src/main/java/io/weaviate/client6/internal/DtoTypeAdapterFactory.java @@ -0,0 +1,61 @@ +package io.weaviate.client6.internal; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +/** + * The purpose of the DtoTypeAdapterFactory is to de-/serialize objects using + * their DTOs. + */ +public class DtoTypeAdapterFactory implements TypeAdapterFactory { + private static final Map, DtoThing> registry = new HashMap<>(); + + public static void register(Class model, Class dto, GsonSerializable> convert) { + registry.putIfAbsent(model, new DtoThing(model, (Class>) dto, convert)); + } + + record DtoThing(Class model, Class> dto, GsonSerializable> convert) { + } + + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + Class cls = type.getRawType(); + if (!registry.containsKey(cls)) { + return null; + } + DtoThing entry = (DtoThing) registry.get(cls); + TypeAdapter> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.dto)); + + return new TypeAdapter() { + + @Override + public T read(JsonReader in) throws IOException { + DTO dto = delegate.read(in); + return dto.toModel(); + } + + @Override + public void write(JsonWriter out, T value) throws IOException { + var dto = entry.convert.toDTO(value); + delegate.write(out, dto); + } + }; + } + + public interface DTO { + M toModel(); + } + + @FunctionalInterface + public interface GsonSerializable> { + D toDTO(M model); + } +} diff --git a/src/main/java/io/weaviate/client6/internal/HttpClient.java b/src/main/java/io/weaviate/client6/internal/HttpClient.java index 624e4a9ca..1d1122b37 100644 --- a/src/main/java/io/weaviate/client6/internal/HttpClient.java +++ b/src/main/java/io/weaviate/client6/internal/HttpClient.java @@ -7,6 +7,9 @@ import org.apache.hc.client5.http.impl.classic.HttpClients; public class HttpClient implements Closeable { + // TODO: move somewhere + // public static final Gson GSON = + public final CloseableHttpClient http; public HttpClient() { diff --git a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java index 5e91085c7..0fefc924e 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java @@ -1,15 +1,10 @@ package io.weaviate.client6.v1.collections; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; -import com.google.gson.Gson; - import io.weaviate.client6.v1.collections.Vectors.NamedVectors; public record CollectionDefinition(String name, List properties, Vectors vectors) { @@ -48,16 +43,4 @@ public Configuration vectors(Consumer named) { options.accept(this); } } - - // JSON serialization --------------- - static CollectionDefinition fromJson(Gson gson, InputStream input) throws IOException { - try (var r = new InputStreamReader(input)) { - var dto = gson.fromJson(r, CollectionDefinitionDTO.class); - return dto.toCollectionDefinition(); - } - } - - public String toJson(Gson gson) { - return gson.toJson(new CollectionDefinitionDTO(this)); - } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java index 8747efccc..9dc91d31a 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java @@ -4,7 +4,9 @@ import com.google.gson.annotations.SerializedName; -public class CollectionDefinitionDTO { +import io.weaviate.client6.internal.DtoTypeAdapterFactory; + +class CollectionDefinitionDTO implements DtoTypeAdapterFactory.DTO { @SerializedName("class") String collection; @@ -14,13 +16,30 @@ public class CollectionDefinitionDTO { @SerializedName("vectorConfig") Vectors vectors; + @SerializedName("vectorIndexType") + private VectorIndex.IndexType vectorIndexType; + + @SerializedName("vectorIndexConfig") + private VectorIndex.IndexingStrategy vectorIndexConfig; + + @SerializedName("vectorizer") + private Vectorizer vectorizer; + public CollectionDefinitionDTO(CollectionDefinition colDef) { this.collection = colDef.name(); this.properties = colDef.properties(); this.vectors = colDef.vectors(); + + var unnamed = this.vectors.getUnnamed(); + if (unnamed.isPresent()) { + var index = unnamed.get(); + this.vectorIndexType = index.type(); + this.vectorIndexConfig = index.configuration(); + this.vectorizer = index.vectorizer(); + } } - CollectionDefinition toCollectionDefinition() { + public CollectionDefinition toModel() { return new CollectionDefinition(collection, properties, vectors); } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java index 9696e38cb..57c8fbd49 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -17,6 +17,7 @@ import com.google.gson.stream.JsonWriter; import io.weaviate.client6.Config; +import io.weaviate.client6.internal.DtoTypeAdapterFactory; import io.weaviate.client6.internal.GrpcClient; import io.weaviate.client6.internal.HttpClient; import io.weaviate.client6.v1.Collection; @@ -32,8 +33,15 @@ public class Collections { private final HttpClient httpClient; private final GrpcClient grpcClient; + static { + DtoTypeAdapterFactory.register(CollectionDefinition.class, + CollectionDefinitionDTO.class, + m -> new CollectionDefinitionDTO(m)); + } + // TODO: use singleton configured in one place private static final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new DtoTypeAdapterFactory()) // TODO: create TypeAdapters via TypeAdapterFactory .registerTypeAdapter(Vectors.class, new TypeAdapter() { Gson gson = new Gson(); @@ -67,7 +75,11 @@ public Vectors read(JsonReader in) throws IOException { @Override public void write(JsonWriter out, Vectorizer value) throws IOException { - gson.toJson(value, value.getClass(), out); + if (value == null) { + out.nullValue(); + } else { + gson.toJson(value, value.getClass(), out); + } } @Override @@ -82,7 +94,7 @@ public void create(String name, Consumer opt ClassicHttpRequest httpPost = ClassicRequestBuilder .post(config.baseUrl() + "/schema") - .setEntity(collection.toJson(gson), ContentType.APPLICATION_JSON) + .setEntity(gson.toJson(collection), ContentType.APPLICATION_JSON) .build(); // TODO: do not expose Apache HttpClient directly diff --git a/src/main/java/io/weaviate/client6/v1/collections/HNSW.java b/src/main/java/io/weaviate/client6/v1/collections/HNSW.java index 11494c11f..cac6b45b4 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/HNSW.java +++ b/src/main/java/io/weaviate/client6/v1/collections/HNSW.java @@ -2,12 +2,22 @@ import java.util.function.Consumer; -public final record HNSW(Distance distance, boolean skip) implements VectorIndex.Configuration { +import io.weaviate.client6.v1.collections.VectorIndex.IndexType; + +public final record HNSW(Distance distance, Boolean skip) implements VectorIndex.IndexingStrategy { + public VectorIndex.IndexType type() { + return IndexType.HNSW; + } + public enum Distance { COSINE; } - public static HNSW with(Consumer options) { + HNSW() { + this(null, null); + } + + static HNSW with(Consumer options) { var opt = new Options(options); return new HNSW(opt.distance, opt.skip); } diff --git a/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java b/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java index d3629d834..ad1160dbf 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java +++ b/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java @@ -7,33 +7,30 @@ public record VectorIndex( @SerializedName("vectorIndexType") IndexType type, @SerializedName("vectorizer") V vectorizer, - @SerializedName("vectorIndexConfig") Configuration configuration) { + @SerializedName("vectorIndexConfig") IndexingStrategy configuration) { public enum IndexType { @SerializedName("hnsw") HNSW; } - public static sealed interface Configuration permits HNSW { + public VectorIndex(IndexingStrategy index, V vectorizer) { + this(index.type(), vectorizer, index); } - private VectorIndex(IndexType type, V vectorizer) { - this(type, vectorizer, null); + public VectorIndex(V vectorizer) { + this(null, vectorizer, null); } - public static VectorIndex bare() { - return new VectorIndex<>(null, Vectorizer.none()); - } - - public static VectorIndex hnsw() { - return new VectorIndex<>(IndexType.HNSW, Vectorizer.none()); - } + public static sealed interface IndexingStrategy permits HNSW { + IndexType type(); - public static VectorIndex hnsw(V vectorizer) { - return new VectorIndex<>(IndexType.HNSW, vectorizer); - } + public static IndexingStrategy hnsw() { + return new HNSW(); + } - public static VectorIndex hnsw(V vectorizer, Consumer options) { - return new VectorIndex<>(IndexType.HNSW, vectorizer, HNSW.with(options)); + public static IndexingStrategy hnsw(Consumer options) { + return HNSW.with(options); + } } } diff --git a/src/test/java/io/weaviate/client6/v1/collections/VectorsTest.java b/src/test/java/io/weaviate/client6/v1/collections/VectorsTest.java new file mode 100644 index 000000000..a61eac8f7 --- /dev/null +++ b/src/test/java/io/weaviate/client6/v1/collections/VectorsTest.java @@ -0,0 +1,144 @@ +package io.weaviate.client6.v1.collections; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParser; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.jparams.junit4.JParamsTestRunner; +import com.jparams.junit4.data.DataMethod; + +import io.weaviate.client6.internal.DtoTypeAdapterFactory; +import io.weaviate.client6.v1.collections.VectorIndex.IndexingStrategy; + +@RunWith(JParamsTestRunner.class) +public class VectorsTest { + // private static final Gson gson = new Gson(); + + static { + DtoTypeAdapterFactory.register(CollectionDefinition.class, CollectionDefinitionDTO.class, + m -> new CollectionDefinitionDTO(m)); + } + private static final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new DtoTypeAdapterFactory()) + // TODO: create TypeAdapters via TypeAdapterFactory + .registerTypeAdapter(Vectors.class, new TypeAdapter() { + Gson gson = new Gson(); + + @Override + public void write(JsonWriter out, Vectors value) throws IOException { + gson.toJson(value.asMap(), Map.class, out); + } + + @Override + public Vectors read(JsonReader in) throws IOException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'read'"); + } + + }) + .registerTypeHierarchyAdapter(Vectorizer.class, new TypeAdapter() { + Gson gson = new Gson(); + + @Override + public void write(JsonWriter out, Vectorizer value) throws IOException { + if (value != null) { + gson.toJson(value, value.getClass(), out); + } else { + out.nullValue(); + } + } + + @Override + public Vectorizer read(JsonReader in) throws IOException { + return Vectorizer.none(); + } + }) + .create(); + + public static Object[][] testCases() { + return new Object[][] { + { + """ + { + "vectorConfig": { + "default": { "vectorizer": { "none":{}}} + } + } + """, + collectionWithVectors(Vectors.of(new VectorIndex<>(Vectorizer.none()))), + new String[] { "vectorConfig" }, + }, + { + """ + { + "vectorConfig": { + "vector-1": { "vectorizer": { "none":{}}}, + "vector-2": { + "vectorizer": { "none":{}}, + "vectorIndexType": "hnsw", + "vectorIndexConfig": {} + } + } + } + """, + collectionWithVectors(Vectors.with(named -> named + .vector("vector-1", new VectorIndex<>(Vectorizer.none())) + .vector("vector-2", new VectorIndex<>(IndexingStrategy.hnsw(), Vectorizer.none())))), + new String[] { "vectorConfig" }, + }, + { + """ + { + "vectorizer": { "none": {}}, + "vectorIndexConfig": { "distance": "COSINE", "skip": true }, + "vectorIndexType": "hnsw" + } + """, + collectionWithVectors(Vectors.unnamed( + new VectorIndex<>( + IndexingStrategy.hnsw(opt -> opt + .distance(HNSW.Distance.COSINE) + .disableIndexation()), + Vectorizer.none()))), + new String[] { "vectorIndexType", "vectorIndexConfig", "vectorizer" }, + }, + }; + } + + @Test + @DataMethod(source = VectorsTest.class, method = "testCases") + public void test_toJson(String want, CollectionDefinition collection, String... compareKeys) { + var got = gson.toJson(collection); + assertEqual(want, got, compareKeys); + } + + private static CollectionDefinition collectionWithVectors(Vectors vectors) { + return new CollectionDefinition("Things", List.of(), vectors); + } + + private void assertEqual(String wantJson, String gotJson, String... compareKeys) { + var want = JsonParser.parseString(wantJson).getAsJsonObject(); + var got = JsonParser.parseString(gotJson).getAsJsonObject(); + + if (compareKeys == null || compareKeys.length == 0) { + Assertions.assertThat(got).isEqualTo(want); + return; + } + + for (var key : compareKeys) { + Assertions.assertThat(got.get(key)) + .isEqualTo(want.get(key)) + .as(key); + } + } +} diff --git a/src/test/java/io/weaviate/internal/DtoTypeAdapterFactoryTest.java b/src/test/java/io/weaviate/internal/DtoTypeAdapterFactoryTest.java new file mode 100644 index 000000000..97fe5474e --- /dev/null +++ b/src/test/java/io/weaviate/internal/DtoTypeAdapterFactoryTest.java @@ -0,0 +1,45 @@ +package io.weaviate.internal; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParser; + +import io.weaviate.client6.internal.DtoTypeAdapterFactory; + +public class DtoTypeAdapterFactoryTest { + static { + DtoTypeAdapterFactory.register(Model.class, ModelDTO.class, m -> new ModelDTO(m)); + } + private static final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new DtoTypeAdapterFactory()).create(); + + @Test + public void test() { + var person = new Model("Josh"); + var wantJson = "{\"nickname\": \"Josh\"}"; + + var gotJson = gson.toJson(person); + Assertions.assertThat(JsonParser.parseString(gotJson)) + .as("serialized") + .isEqualTo(JsonParser.parseString(wantJson)); + + var gotModel = gson.fromJson(gotJson, Model.class); + Assertions.assertThat(gotModel).as("deserialized").isEqualTo(person); + } +} + +record Model(String name) { +} + +record ModelDTO(String nickname) implements DtoTypeAdapterFactory.DTO { + ModelDTO(Model m) { + this(m.name()); + } + + @Override + public Model toModel() { + return new Model(nickname); + } +} From b103ee8cb8010a5f9fd05763b57672774cd75cad Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sun, 16 Mar 2025 18:01:50 +0100 Subject: [PATCH 14/17] chore: remove redundant TypeAdapter --- .../client6/v1/collections/Collections.java | 32 ------------------- .../client6/v1/collections/VectorsTest.java | 17 ---------- 2 files changed, 49 deletions(-) diff --git a/src/main/java/io/weaviate/client6/v1/collections/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java index 57c8fbd49..ac94898ec 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -21,8 +21,6 @@ import io.weaviate.client6.internal.GrpcClient; import io.weaviate.client6.internal.HttpClient; import io.weaviate.client6.v1.Collection; -import io.weaviate.client6.v1.collections.CollectionDefinition.Configuration; -import io.weaviate.client6.v1.collections.VectorIndex.IndexType; import lombok.AllArgsConstructor; @AllArgsConstructor @@ -48,18 +46,6 @@ public class Collections { @Override public void write(JsonWriter out, Vectors value) throws IOException { - var unnamed = value.getUnnamed(); - if (unnamed.isPresent()) { - var index = unnamed.get(); - out.name("vectorIndexType"); - gson.toJson(index.type(), IndexType.class, out); - out.name("vectorizer"); - gson.toJson(index.vectorizer(), Vectorizer.class, out); - out.name("vectorIndexConfig"); - gson.toJson(index.configuration(), Configuration.class, out); - return; - } - gson.toJson(value.asMap(), Map.class, out); } @@ -68,24 +54,6 @@ public Vectors read(JsonReader in) throws IOException { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'read'"); } - - }) - .registerTypeHierarchyAdapter(Vectorizer.class, new TypeAdapter() { - Gson gson = new Gson(); - - @Override - public void write(JsonWriter out, Vectorizer value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - gson.toJson(value, value.getClass(), out); - } - } - - @Override - public Vectorizer read(JsonReader in) throws IOException { - return Vectorizer.none(); - } }) .create(); diff --git a/src/test/java/io/weaviate/client6/v1/collections/VectorsTest.java b/src/test/java/io/weaviate/client6/v1/collections/VectorsTest.java index a61eac8f7..37426ac75 100644 --- a/src/test/java/io/weaviate/client6/v1/collections/VectorsTest.java +++ b/src/test/java/io/weaviate/client6/v1/collections/VectorsTest.java @@ -46,23 +46,6 @@ public Vectors read(JsonReader in) throws IOException { } }) - .registerTypeHierarchyAdapter(Vectorizer.class, new TypeAdapter() { - Gson gson = new Gson(); - - @Override - public void write(JsonWriter out, Vectorizer value) throws IOException { - if (value != null) { - gson.toJson(value, value.getClass(), out); - } else { - out.nullValue(); - } - } - - @Override - public Vectorizer read(JsonReader in) throws IOException { - return Vectorizer.none(); - } - }) .create(); public static Object[][] testCases() { From aa5ba40b5ff07378af1c4d1bfe4211b65214cc31 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Mon, 17 Mar 2025 11:03:33 +0100 Subject: [PATCH 15/17] chore: cleanup DtoTypeAdapterFactory and add unit tests --- .../internal/DtoTypeAdapterFactory.java | 91 ++++++++++++++----- .../collections/CollectionDefinitionDTO.java | 2 +- .../client6/v1/collections/Collections.java | 3 +- .../internal/DtoTypeAdapterFactoryTest.java | 77 +++++++++++----- 4 files changed, 127 insertions(+), 46 deletions(-) diff --git a/src/main/java/io/weaviate/client6/internal/DtoTypeAdapterFactory.java b/src/main/java/io/weaviate/client6/internal/DtoTypeAdapterFactory.java index 7f88bbc9f..812f6dddd 100644 --- a/src/main/java/io/weaviate/client6/internal/DtoTypeAdapterFactory.java +++ b/src/main/java/io/weaviate/client6/internal/DtoTypeAdapterFactory.java @@ -12,50 +12,97 @@ import com.google.gson.stream.JsonWriter; /** - * The purpose of the DtoTypeAdapterFactory is to de-/serialize objects using - * their DTOs. + * DtoTypeAdapterFactory de-/serializes objects using their registerred DTOs. + * + *

+ * DTO classes must implement {@link Dto}, which produces the original model. + * Meanwhile, models do not need to be modified, to avoid leaking + * de-/serialization details. + * + *

+ * Usage: + * + *

{@code
+ * public class HttpHanlder {
+ *   static {
+ *     DtoTypeAdapterFactory.register(
+ *         MyDomainObject.class,
+ *         MyDtoObject.class,
+ *         domain -> new MyDtoObject(domain));
+ *   }
+ *   static final Gson gson = new GsonBuilder()
+ *       .registerTypeAdapterFactory(new DtoTypeAdapterFactory())
+ *       .create();
+ * }
+ * }
*/ public class DtoTypeAdapterFactory implements TypeAdapterFactory { - private static final Map, DtoThing> registry = new HashMap<>(); + private static boolean locked = false; + private static final Map, Pair> registry = new HashMap<>(); - public static void register(Class model, Class dto, GsonSerializable> convert) { - registry.putIfAbsent(model, new DtoThing(model, (Class>) dto, convert)); + /** + * Register a model-DTO pair. + * + *

+ * Only one DTO can be registerred per model. + * Subsequent registrations will be ignored. + */ + public static > void register(Class model, Class dto, + ModelConverter> convert) { + registry.putIfAbsent(model, new Pair(dto, convert)); } - record DtoThing(Class model, Class> dto, GsonSerializable> convert) { + /** + * Get model-DTO pair for the provided model class. Returns null if no pair is + * registerred. In this case {@link #create} should also return null. + * + *

+ * Conversion to {@code Pair} is safe, as entries to {@link #registry} + * can only be added via {@link #register}, which is type-safe. + */ + @SuppressWarnings("unchecked") + private static > Pair getPair(TypeToken type) { + var cls = type.getRawType(); + if (!registry.containsKey(cls)) { + return null; + } + return (Pair) registry.get(cls); + } + + /** Dto produces a domain model. */ + public interface Dto { + M toModel(); + } + + /** ModelConverter converts domain model to a DTO. */ + @FunctionalInterface + public interface ModelConverter> { + D toDTO(M model); + } + + record Pair>(Class dto, ModelConverter> convert) { } @Override public TypeAdapter create(Gson gson, TypeToken type) { - Class cls = type.getRawType(); - if (!registry.containsKey(cls)) { + var pair = getPair(type); + if (pair == null) { return null; } - DtoThing entry = (DtoThing) registry.get(cls); - TypeAdapter> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.dto)); - + var delegate = gson.getDelegateAdapter(this, TypeToken.get(pair.dto)); return new TypeAdapter() { @Override public T read(JsonReader in) throws IOException { - DTO dto = delegate.read(in); + var dto = delegate.read(in); return dto.toModel(); } @Override public void write(JsonWriter out, T value) throws IOException { - var dto = entry.convert.toDTO(value); + var dto = pair.convert.toDTO(value); delegate.write(out, dto); } }; } - - public interface DTO { - M toModel(); - } - - @FunctionalInterface - public interface GsonSerializable> { - D toDTO(M model); - } } diff --git a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java index 9dc91d31a..2c6cd5c85 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinitionDTO.java @@ -6,7 +6,7 @@ import io.weaviate.client6.internal.DtoTypeAdapterFactory; -class CollectionDefinitionDTO implements DtoTypeAdapterFactory.DTO { +class CollectionDefinitionDTO implements DtoTypeAdapterFactory.Dto { @SerializedName("class") String collection; diff --git a/src/main/java/io/weaviate/client6/v1/collections/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java index ac94898ec..e58b47373 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -32,7 +32,8 @@ public class Collections { private final GrpcClient grpcClient; static { - DtoTypeAdapterFactory.register(CollectionDefinition.class, + DtoTypeAdapterFactory.register( + CollectionDefinition.class, CollectionDefinitionDTO.class, m -> new CollectionDefinitionDTO(m)); } diff --git a/src/test/java/io/weaviate/internal/DtoTypeAdapterFactoryTest.java b/src/test/java/io/weaviate/internal/DtoTypeAdapterFactoryTest.java index 97fe5474e..85cf85da6 100644 --- a/src/test/java/io/weaviate/internal/DtoTypeAdapterFactoryTest.java +++ b/src/test/java/io/weaviate/internal/DtoTypeAdapterFactoryTest.java @@ -2,44 +2,77 @@ import org.assertj.core.api.Assertions; import org.junit.Test; +import org.junit.runner.RunWith; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParser; +import com.jparams.junit4.JParamsTestRunner; +import com.jparams.junit4.data.DataMethod; import io.weaviate.client6.internal.DtoTypeAdapterFactory; +@RunWith(JParamsTestRunner.class) public class DtoTypeAdapterFactoryTest { - static { - DtoTypeAdapterFactory.register(Model.class, ModelDTO.class, m -> new ModelDTO(m)); + /** Person should be serialized to PersonDto. */ + record Person(String name) { } - private static final Gson gson = new GsonBuilder().registerTypeAdapterFactory(new DtoTypeAdapterFactory()).create(); - @Test - public void test() { - var person = new Model("Josh"); - var wantJson = "{\"nickname\": \"Josh\"}"; + record PersonDto(String nickname) implements DtoTypeAdapterFactory.Dto { + PersonDto(Person p) { + this(p.name); + } - var gotJson = gson.toJson(person); - Assertions.assertThat(JsonParser.parseString(gotJson)) - .as("serialized") - .isEqualTo(JsonParser.parseString(wantJson)); + @Override + public Person toModel() { + return new Person(nickname); + } + } + + /** Car's DTO is a nested record. */ + record Car(String brand) { + record CarDto(String manufacturer, Integer version) implements DtoTypeAdapterFactory.Dto { + CarDto(Car c) { + this(c.brand, 1); + } - var gotModel = gson.fromJson(gotJson, Model.class); - Assertions.assertThat(gotModel).as("deserialized").isEqualTo(person); + @Override + public Car toModel() { + return new Car(manufacturer); + } + } } -} -record Model(String name) { -} + /** Normal does not have a DTO and should be serialized as usual. */ + record Normal(String key, String value) { + } -record ModelDTO(String nickname) implements DtoTypeAdapterFactory.DTO { - ModelDTO(Model m) { - this(m.name()); + static { + DtoTypeAdapterFactory.register(Person.class, PersonDto.class, m -> new PersonDto(m)); + DtoTypeAdapterFactory.register(Car.class, Car.CarDto.class, m -> new Car.CarDto(m)); } - @Override - public Model toModel() { - return new Model(nickname); + private static final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new DtoTypeAdapterFactory()) + .create(); + + public static Object[][] testCases() { + return new Object[][] { + { new Person("Josh"), "{\"nickname\": \"Josh\"}" }, + { new Car("Porsche"), "{\"manufacturer\": \"Porsche\", \"version\": 1}" }, + { new Normal("foo", "bar"), "{\"key\": \"foo\", \"value\": \"bar\"}" }, + }; + } + + @Test + @DataMethod(source = DtoTypeAdapterFactoryTest.class, method = "testCases") + public void testRoundtrip(Object model, String wantJson) { + var gotJson = gson.toJson(model); + Assertions.assertThat(JsonParser.parseString(gotJson)) + .as("serialized") + .isEqualTo(JsonParser.parseString(wantJson)); + + var deserialized = gson.fromJson(gotJson, model.getClass()); + Assertions.assertThat(deserialized).as("deserialized").isEqualTo(model); } } From 7fd3ac2ae70a798b832dd6817f834d951f9ad0d7 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 18 Mar 2025 13:54:07 +0100 Subject: [PATCH 16/17] feat: get collection config and delete collections A lot of WIP code here, all in the name of making rapid iterations --- src/it/java/io/weaviate/ConcurrentTest.java | 2 +- .../weaviate/client6/v1/CollectionsITest.java | 5 - .../v1/collections/CollectionDefinition.java | 7 +- .../client6/v1/collections/Collections.java | 93 +++++++++++++++++-- .../client6/v1/collections/Property.java | 2 + .../client6/v1/collections/Vectors.java | 4 + 6 files changed, 100 insertions(+), 13 deletions(-) delete mode 100644 src/it/java/io/weaviate/client6/v1/CollectionsITest.java diff --git a/src/it/java/io/weaviate/ConcurrentTest.java b/src/it/java/io/weaviate/ConcurrentTest.java index 570cffa36..2e2036d18 100644 --- a/src/it/java/io/weaviate/ConcurrentTest.java +++ b/src/it/java/io/weaviate/ConcurrentTest.java @@ -34,7 +34,7 @@ public abstract class ConcurrentTest { protected String ns(String value) { String cls = getClass().getSimpleName(); String method = currentTest.getMethodName(); - return cls + "." + method + "-" + value; + return cls + "_" + method + "_" + value; } /** Appends random characters to create unique value. */ diff --git a/src/it/java/io/weaviate/client6/v1/CollectionsITest.java b/src/it/java/io/weaviate/client6/v1/CollectionsITest.java deleted file mode 100644 index d83847693..000000000 --- a/src/it/java/io/weaviate/client6/v1/CollectionsITest.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.weaviate.client6.v1; - -public class CollectionsITest { - -} diff --git a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java index 0fefc924e..b599f4ceb 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java +++ b/src/main/java/io/weaviate/client6/v1/collections/CollectionDefinition.java @@ -24,8 +24,13 @@ public Configuration properties(Property... properties) { return this; } + public Configuration vectors(Vectors vectors) { + this.vectors = vectors; + return this; + } + public Configuration vector(VectorIndex vector) { - this.vectors = new Vectors(vector); + this.vectors = Vectors.of(vector); return this; } diff --git a/src/main/java/io/weaviate/client6/v1/collections/Collections.java b/src/main/java/io/weaviate/client6/v1/collections/Collections.java index e58b47373..0a2b8e97d 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Collections.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Collections.java @@ -1,7 +1,10 @@ package io.weaviate.client6.v1.collections; import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Type; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import org.apache.hc.core5.http.ClassicHttpRequest; @@ -10,8 +13,15 @@ import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; +import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -21,6 +31,7 @@ import io.weaviate.client6.internal.GrpcClient; import io.weaviate.client6.internal.HttpClient; import io.weaviate.client6.v1.Collection; +import io.weaviate.client6.v1.collections.VectorIndex.IndexingStrategy; import lombok.AllArgsConstructor; @AllArgsConstructor @@ -35,15 +46,52 @@ public class Collections { DtoTypeAdapterFactory.register( CollectionDefinition.class, CollectionDefinitionDTO.class, - m -> new CollectionDefinitionDTO(m)); + m -> { + return new CollectionDefinitionDTO(m); + }); + } + + // Gson cannot deserialize interfaces: + // https://stackoverflow.com/a/49871339/14726116 + private static class IndexingStrategySerde + implements JsonDeserializer, JsonSerializer { + + @Override + public IndexingStrategy deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return IndexingStrategy.hnsw(); + } + + @Override + public JsonElement serialize(IndexingStrategy src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src); + } + } + + // Gson cannot deserialize interfaces: + // https://stackoverflow.com/a/49871339/14726116 + private static class VectorizerSerde + implements JsonDeserializer, JsonSerializer { + + @Override + public Vectorizer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return Vectorizer.none(); + } + + @Override + public JsonElement serialize(Vectorizer src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src); + } } - // TODO: use singleton configured in one place private static final Gson gson = new GsonBuilder() .registerTypeAdapterFactory(new DtoTypeAdapterFactory()) - // TODO: create TypeAdapters via TypeAdapterFactory .registerTypeAdapter(Vectors.class, new TypeAdapter() { - Gson gson = new Gson(); + Gson gson = new GsonBuilder() + .registerTypeAdapter(Vectorizer.class, new VectorizerSerde()) + .registerTypeAdapter(IndexingStrategy.class, new IndexingStrategySerde()) + .create(); @Override public void write(JsonWriter out, Vectors value) throws IOException { @@ -52,8 +100,10 @@ public void write(JsonWriter out, Vectors value) throws IOException { @Override public Vectors read(JsonReader in) throws IOException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'read'"); + Map> vectors = gson.fromJson(in, + new TypeToken>>() { + }.getType()); + return Vectors.of(vectors); } }) .create(); @@ -77,6 +127,37 @@ public void create(String name, Consumer opt }); } + public Optional getConfig(String name) throws IOException { + ClassicHttpRequest httpGet = ClassicRequestBuilder + .get(config.baseUrl() + "/schema/" + name) + .build(); + + return httpClient.http.execute(httpGet, response -> { + if (response.getCode() == HttpStatus.SC_NOT_FOUND) { + return Optional.empty(); + } + try (var r = new InputStreamReader(response.getEntity().getContent())) { + var collection = gson.fromJson(r, CollectionDefinition.class); + return Optional.ofNullable(collection); + } + }); + } + + public void delete(String name) throws IOException { + ClassicHttpRequest httpDelete = ClassicRequestBuilder + .delete(config.baseUrl() + "/schema/" + name) + .build(); + + httpClient.http.execute(httpDelete, response -> { + var entity = response.getEntity(); + if (response.getCode() != HttpStatus.SC_SUCCESS) { // Does not return 201 + var message = EntityUtils.toString(entity); + throw new RuntimeException("HTTP " + response.getCode() + ": " + message); + } + return null; + }); + } + public Collection> use(String name) { return new Collection<>(name, config, grpcClient, httpClient); } diff --git a/src/main/java/io/weaviate/client6/v1/collections/Property.java b/src/main/java/io/weaviate/client6/v1/collections/Property.java index a0b6b88d7..81d23a5df 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Property.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Property.java @@ -13,10 +13,12 @@ public class Property { @SerializedName("dataType") public final List dataTypes; + /** Add text property with default configuration. */ public static Property text(String name) { return new Property(name, DataType.TEXT); } + /** Add integer property with default configuration. */ public static Property integer(String name) { return new Property(name, DataType.INT); } diff --git a/src/main/java/io/weaviate/client6/v1/collections/Vectors.java b/src/main/java/io/weaviate/client6/v1/collections/Vectors.java index 1c6e0a481..345442914 100644 --- a/src/main/java/io/weaviate/client6/v1/collections/Vectors.java +++ b/src/main/java/io/weaviate/client6/v1/collections/Vectors.java @@ -23,6 +23,10 @@ public static Vectors of(VectorIndex vector) { return new Vectors(DEFAULT, vector); } + public static Vectors of(Map> vectors) { + return new Vectors(vectors); + } + public static Vectors with(Consumer named) { var vectors = new NamedVectors(named); return new Vectors(vectors.namedVectors); From a3d83f5afbd29c2ead069b0f5e684759592c7661 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 18 Mar 2025 14:48:50 +0100 Subject: [PATCH 17/17] test: add unit tests for collections --- .../v1/collections/CollectionsITest.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/it/java/io/weaviate/client6/v1/collections/CollectionsITest.java diff --git a/src/it/java/io/weaviate/client6/v1/collections/CollectionsITest.java b/src/it/java/io/weaviate/client6/v1/collections/CollectionsITest.java new file mode 100644 index 000000000..dd50d69e1 --- /dev/null +++ b/src/it/java/io/weaviate/client6/v1/collections/CollectionsITest.java @@ -0,0 +1,41 @@ +package io.weaviate.client6.v1.collections; + +import java.io.IOException; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import io.weaviate.ConcurrentTest; +import io.weaviate.client6.WeaviateClient; +import io.weaviate.client6.v1.collections.VectorIndex.IndexType; +import io.weaviate.client6.v1.collections.VectorIndex.IndexingStrategy; +import io.weaviate.containers.Container; + +public class CollectionsITest extends ConcurrentTest { + private static WeaviateClient client = Container.WEAVIATE.getClient(); + + @Test + public void testCreateGetDelete() throws IOException { + var collectionName = ns("Things_1"); + client.collections.create(collectionName, + col -> col + .properties(Property.text("username"), Property.integer("age")) + .vector(new VectorIndex<>(IndexingStrategy.hnsw(), Vectorizer.none()))); + + var thingsCollection = client.collections.getConfig(collectionName); + + Assertions.assertThat(thingsCollection).get() + .hasFieldOrPropertyWithValue("name", collectionName) + .extracting(CollectionDefinition::vectors).extracting(Vectors::getDefault) + .as("default vector").satisfies(defaultVector -> { + Assertions.assertThat(defaultVector).extracting(VectorIndex::vectorizer) + .as("has none vectorizer").isInstanceOf(NoneVectorizer.class); + Assertions.assertThat(defaultVector).extracting(VectorIndex::configuration) + .as("has hnsw index").returns(IndexType.HNSW, IndexingStrategy::type); + }); + + client.collections.delete(collectionName); + var noCollection = client.collections.getConfig(collectionName); + Assertions.assertThat(noCollection).as("after delete").isEmpty(); + } +}