uuids) {
+
+ public Reference(String collection, String uuid) {
+ this(collection, List.of(uuid));
+ }
+
+ /**
+ * Create reference to objects by their UUIDs.
+ *
+ * Weaviate will search each of the existing collections to identify
+ * the objects before inserting the references, so this may include
+ * some performance overhead.
+ */
+ public static Reference uuids(String... uuids) {
+ return new Reference(null, Arrays.asList(uuids));
+ }
+
+ /** Create references to {@link WeaviateObject}. */
+ public static Reference[] objects(WeaviateObject>... objects) {
+ return Arrays.stream(objects)
+ .map(o -> new Reference(o.collection(), o.metadata().id()))
+ .toArray(Reference[]::new);
+ }
+
+ /** Create references to objects in a collection by their UUIDs. */
+ public static Reference collection(String collection, String... uuids) {
+ return new Reference(collection, Arrays.asList(uuids));
+ }
+
+ // TODO: put this in a type adapter.
+ /** writeValue assumes an array has been started will be ended by the caller. */
+ public void writeValue(JsonWriter w) throws IOException {
+ for (var uuid : uuids) {
+ w.beginObject();
+ w.name("beacon");
+ w.value(toBeacon(uuid));
+ w.endObject();
+ }
+ }
+
+ private String toBeacon(String uuid) {
+ var beacon = "weaviate://localhost/";
+ if (collection != null) {
+ beacon += collection + "/";
+ }
+ beacon += uuid;
+ return beacon;
+ }
+}
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 5db348263..b8aea7f0c 100644
--- a/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java
+++ b/src/main/java/io/weaviate/client6/v1/collections/VectorIndex.java
@@ -29,7 +29,7 @@ public static IndexingStrategy hnsw() {
return new HNSW();
}
- public static IndexingStrategy hnsw(Consumer options) {
+ public static IndexingStrategy hnsw(Consumer options) {
return 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
index ad9c4260f..d421d926a 100644
--- a/src/main/java/io/weaviate/client6/v1/collections/Vectorizer.java
+++ b/src/main/java/io/weaviate/client6/v1/collections/Vectorizer.java
@@ -1,8 +1,18 @@
package io.weaviate.client6.v1.collections;
+import java.util.function.Consumer;
+
// This class is WIP, I haven't decided how to structure it yet.
public abstract class Vectorizer {
public static NoneVectorizer none() {
return new NoneVectorizer();
}
+
+ public static ContextionaryVectorizer contextionary() {
+ return ContextionaryVectorizer.of();
+ }
+
+ public static ContextionaryVectorizer contextionary(Consumer fn) {
+ return ContextionaryVectorizer.of(fn);
+ }
}
diff --git a/src/main/java/io/weaviate/client6/v1/collections/aggregate/WeaviateAggregate.java b/src/main/java/io/weaviate/client6/v1/collections/aggregate/AggregateClient.java
similarity index 95%
rename from src/main/java/io/weaviate/client6/v1/collections/aggregate/WeaviateAggregate.java
rename to src/main/java/io/weaviate/client6/v1/collections/aggregate/AggregateClient.java
index 73474fb7b..cfc774eae 100644
--- a/src/main/java/io/weaviate/client6/v1/collections/aggregate/WeaviateAggregate.java
+++ b/src/main/java/io/weaviate/client6/v1/collections/aggregate/AggregateClient.java
@@ -5,13 +5,13 @@
import io.weaviate.client6.internal.GrpcClient;
import io.weaviate.client6.internal.codec.grpc.v1.AggregateMarshaler;
import io.weaviate.client6.internal.codec.grpc.v1.AggregateUnmarshaler;
-import io.weaviate.client6.v1.query.NearVector;
+import io.weaviate.client6.v1.collections.query.NearVector;
-public class WeaviateAggregate {
+public class AggregateClient {
private final String collectionName;
private final GrpcClient grpcClient;
- public WeaviateAggregate(String collectionName, GrpcClient grpc) {
+ public AggregateClient(String collectionName, GrpcClient grpc) {
this.collectionName = collectionName;
this.grpcClient = grpc;
}
@@ -55,11 +55,11 @@ public AggregateResponse nearVector(
public AggregateGroupByResponse nearVector(
Float[] vector,
+ Consumer nearVectorOptions,
AggregateGroupByRequest.GroupBy groupBy,
Consumer options) {
var aggregation = AggregateRequest.with(collectionName, options);
- var nearVector = NearVector.with(vector, opt -> {
- });
+ var nearVector = NearVector.with(vector, nearVectorOptions);
var req = new AggregateMarshaler(aggregation.collectionName())
.addAggregation(aggregation)
@@ -72,11 +72,11 @@ public AggregateGroupByResponse nearVector(
public AggregateGroupByResponse nearVector(
Float[] vector,
- Consumer nearVectorOptions,
AggregateGroupByRequest.GroupBy groupBy,
Consumer options) {
var aggregation = AggregateRequest.with(collectionName, options);
- var nearVector = NearVector.with(vector, nearVectorOptions);
+ var nearVector = NearVector.with(vector, opt -> {
+ });
var req = new AggregateMarshaler(aggregation.collectionName())
.addAggregation(aggregation)
diff --git a/src/main/java/io/weaviate/client6/v1/collections/aggregate/TopOccurrences.java b/src/main/java/io/weaviate/client6/v1/collections/aggregate/TopOccurrences.java
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/main/java/io/weaviate/client6/v1/data/ConsistencyLevel.java b/src/main/java/io/weaviate/client6/v1/collections/data/ConsistencyLevel.java
similarity index 51%
rename from src/main/java/io/weaviate/client6/v1/data/ConsistencyLevel.java
rename to src/main/java/io/weaviate/client6/v1/collections/data/ConsistencyLevel.java
index 1011a7a02..3347b012c 100644
--- a/src/main/java/io/weaviate/client6/v1/data/ConsistencyLevel.java
+++ b/src/main/java/io/weaviate/client6/v1/collections/data/ConsistencyLevel.java
@@ -1,4 +1,4 @@
-package io.weaviate.client6.v1.data;
+package io.weaviate.client6.v1.collections.data;
public enum ConsistencyLevel {
ONE, QUORUM, ALL
diff --git a/src/main/java/io/weaviate/client6/v1/collections/data/DataClient.java b/src/main/java/io/weaviate/client6/v1/collections/data/DataClient.java
new file mode 100644
index 000000000..e8878e1e8
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/collections/data/DataClient.java
@@ -0,0 +1,233 @@
+package io.weaviate.client6.v1.collections.data;
+
+import java.io.IOException;
+import java.time.OffsetDateTime;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+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.grpc.protocol.v1.WeaviateProtoBase.Vectors.VectorType;
+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.PropertiesResult;
+import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.SearchRequest;
+import io.weaviate.client6.internal.GRPC;
+import io.weaviate.client6.internal.GrpcClient;
+import io.weaviate.client6.internal.HttpClient;
+import io.weaviate.client6.v1.collections.object.ObjectMetadata;
+import io.weaviate.client6.v1.collections.object.ObjectReference;
+import io.weaviate.client6.v1.collections.object.Vectors;
+import io.weaviate.client6.v1.collections.object.WeaviateObject;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+public class DataClient {
+ // 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;
+ private final HttpClient httpClient;
+ private final GrpcClient grpcClient;
+
+ public WeaviateObject insert(T properties) throws IOException {
+ return insert(properties, opt -> {
+ });
+ }
+
+ public WeaviateObject insert(T properties, Consumer> fn) throws IOException {
+ return insert(InsertObjectRequest.of(collectionName, properties, fn));
+ }
+
+ public WeaviateObject insert(InsertObjectRequest request) throws IOException {
+ ClassicHttpRequest httpPost = ClassicRequestBuilder
+ .post(config.baseUrl() + "/objects")
+ .setEntity(request.serialize(gson), ContentType.APPLICATION_JSON)
+ .build();
+
+ 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());
+ });
+ }
+
+ public Optional> get(String id) throws IOException {
+ return get(id, q -> {
+ });
+ }
+
+ public Optional> get(String id, Consumer fn) throws IOException {
+ return findById(FetchByIdRequest.of(collectionName, id, fn));
+ }
+
+ private Optional> findById(FetchByIdRequest request) {
+ var req = SearchRequest.newBuilder();
+ req.setUses127Api(true);
+ req.setUses125Api(true);
+ req.setUses123Api(true);
+ request.appendTo(req);
+ var result = grpcClient.grpc.search(req.build());
+ var objects = result.getResultsList().stream().map(r -> {
+ var tempObj = readPropertiesResult(r.getProperties());
+ MetadataResult meta = r.getMetadata();
+ Vectors vectors;
+ if (!meta.getVectorBytes().isEmpty()) {
+ vectors = Vectors.of(GRPC.fromByteString(meta.getVectorBytes()));
+ } else {
+ vectors = Vectors.of(meta.getVectorsList().stream().collect(
+ Collectors.toMap(
+ io.weaviate.client6.grpc.protocol.v1.WeaviateProtoBase.Vectors::getName,
+ v -> {
+ if (v.getType().equals(VectorType.VECTOR_TYPE_SINGLE_FP32)) {
+ return GRPC.fromByteString(v.getVectorBytes());
+ } else {
+ return GRPC.fromByteStringMulti(v.getVectorBytes());
+ }
+ })));
+ }
+ var metadata = new ObjectMetadata(meta.getId(), vectors);
+ return new WeaviateObject<>(
+ tempObj.collection(),
+ tempObj.properties(),
+ tempObj.references(),
+ metadata);
+ }).toList();
+ if (objects.isEmpty()) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable((WeaviateObject) objects.get(0));
+ }
+
+ private static WeaviateObject> readPropertiesResult(PropertiesResult res) {
+ var collection = res.getTargetCollection();
+ var objectProperties = convertProtoMap(res.getNonRefProps().getFieldsMap());
+
+ // In case a reference is multi-target, there will be a separate
+ // "reference property" for each of the targets, so instead of
+ // `collect` we need to `reduce` the map, merging related references
+ // as we go.
+ // I.e. { "ref": A-1 } , { "ref": B-1 } => { "ref": [A-1, B-1] }
+ var referenceProperties = res.getRefPropsList().stream().reduce(
+ new HashMap(),
+ (map, ref) -> {
+ var refObjects = ref.getPropertiesList().stream()
+ .map(DataClient::readPropertiesResult)
+ .toList();
+
+ // Merge ObjectReferences by joining the underlying WeaviateObjects.
+ map.merge(
+ ref.getPropName(),
+ new ObjectReference((List>) refObjects),
+ (left, right) -> {
+ var joined = Stream.concat(
+ left.objects().stream(),
+ right.objects().stream()).toList();
+ return new ObjectReference(joined);
+ });
+ return map;
+ },
+ (left, right) -> {
+ left.putAll(right);
+ return left;
+ });
+
+ MetadataResult meta = res.getMetadata();
+ Vectors vectors;
+ if (meta.getVectorBytes() != null) {
+ vectors = Vectors.of(GRPC.fromByteString(meta.getVectorBytes()));
+ } else {
+ vectors = Vectors.of(meta.getVectorsList().stream().collect(
+ Collectors.toMap(
+ io.weaviate.client6.grpc.protocol.v1.WeaviateProtoBase.Vectors::getName,
+ v -> {
+ if (v.getType().equals(VectorType.VECTOR_TYPE_MULTI_FP32)) {
+ return GRPC.fromByteString(v.getVectorBytes());
+ } else {
+ return GRPC.fromByteStringMulti(v.getVectorBytes());
+ }
+ })));
+ }
+ var metadata = new ObjectMetadata(meta.getId(), vectors);
+ return new WeaviateObject<>(collection, objectProperties, referenceProperties, metadata);
+ }
+
+ /*
+ * Convert Map to Map such that can be
+ * (de-)serialized by {@link Gson}.
+ */
+ private static Map convertProtoMap(Map map) {
+ return map.entrySet().stream()
+ // We cannot use Collectors.toMap() here, because convertProtoValue may
+ // return null (a collection property can be null), which breaks toMap().
+ // See: https://bugs.openjdk.org/browse/JDK-8148463
+ .collect(
+ HashMap::new,
+ (m, e) -> m.put(e.getKey(), convertProtoValue(e.getValue())),
+ HashMap::putAll);
+ }
+
+ /**
+ * 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.hasNullValue()) {
+ // return value.getNullValue();
+ return null;
+ } else 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;
+ }
+
+ public void delete(String id) throws IOException {
+ try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
+ ClassicHttpRequest httpGet = ClassicRequestBuilder
+ .delete(config.baseUrl() + "/objects/" + collectionName + "/" + id)
+ .build();
+
+ httpClient.http.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/collections/data/FetchByIdRequest.java b/src/main/java/io/weaviate/client6/v1/collections/data/FetchByIdRequest.java
new file mode 100644
index 000000000..28cb8635f
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/collections/data/FetchByIdRequest.java
@@ -0,0 +1,112 @@
+package io.weaviate.client6.v1.collections.data;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoBase.FilterTarget;
+import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoBase.Filters;
+import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoBase.Filters.Operator;
+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.RefPropertiesRequest;
+import io.weaviate.client6.grpc.protocol.v1.WeaviateProtoSearchGet.SearchRequest;
+import io.weaviate.client6.v1.collections.query.QueryReference;
+
+public record FetchByIdRequest(
+ String collection,
+ String id,
+ boolean includeVector,
+ List includeVectors,
+ List returnProperties,
+ List returnReferences) {
+
+ public FetchByIdRequest(Builder options) {
+ this(
+ options.collection,
+ options.uuid,
+ options.includeVector,
+ options.includeVectors,
+ options.returnProperties,
+ options.returnReferences);
+ }
+
+ public static FetchByIdRequest of(String collection, String uuid, Consumer fn) {
+ var builder = new Builder(collection, uuid);
+ fn.accept(builder);
+ return new FetchByIdRequest(builder);
+ }
+
+ public static class Builder {
+ private final String collection;
+ private final String uuid;
+
+ public Builder(String collection, String uuid) {
+ this.collection = collection;
+ this.uuid = uuid;
+ }
+
+ private boolean includeVector;
+ private List includeVectors = new ArrayList<>();
+ private List returnProperties = new ArrayList<>();
+ private List returnReferences = new ArrayList<>();
+
+ public final Builder includeVector() {
+ this.includeVector = true;
+ return this;
+ }
+
+ public final Builder includeVectors(String... vectors) {
+ this.includeVectors = Arrays.asList(vectors);
+ return this;
+ }
+
+ public final Builder returnProperties(String... properties) {
+ this.returnProperties = Arrays.asList(properties);
+ return this;
+ }
+
+ public final Builder returnReferences(QueryReference... references) {
+ this.returnReferences = Arrays.asList(references);
+ return this;
+ }
+
+ }
+
+ void appendTo(SearchRequest.Builder req) {
+ req.setLimit(1);
+ req.setCollection(collection);
+
+ req.setFilters(Filters.newBuilder()
+ .setTarget(FilterTarget.newBuilder().setProperty("_id"))
+ .setValueText(id)
+ .setOperator(Operator.OPERATOR_EQUAL));
+
+ if (!returnProperties.isEmpty() || !returnReferences.isEmpty()) {
+ var properties = PropertiesRequest.newBuilder();
+
+ if (!returnProperties.isEmpty()) {
+ properties.addAllNonRefProperties(returnProperties);
+ }
+
+ if (!returnReferences.isEmpty()) {
+ returnReferences.forEach(r -> {
+ var references = RefPropertiesRequest.newBuilder();
+ r.appendTo(references);
+ properties.addRefProperties(references);
+ });
+ }
+ req.setProperties(properties);
+ }
+
+ // Always request UUID back in this request.
+ var metadata = MetadataRequest.newBuilder().setUuid(true);
+ if (includeVector) {
+ metadata.setVector(true);
+ } else if (!includeVectors.isEmpty()) {
+ metadata.addAllVectors(includeVectors);
+ }
+ req.setMetadata(metadata);
+ }
+}
diff --git a/src/main/java/io/weaviate/client6/v1/data/GetParameters.java b/src/main/java/io/weaviate/client6/v1/collections/data/GetParameters.java
similarity index 97%
rename from src/main/java/io/weaviate/client6/v1/data/GetParameters.java
rename to src/main/java/io/weaviate/client6/v1/collections/data/GetParameters.java
index 819ffc09c..a0e4afd43 100644
--- a/src/main/java/io/weaviate/client6/v1/data/GetParameters.java
+++ b/src/main/java/io/weaviate/client6/v1/collections/data/GetParameters.java
@@ -1,4 +1,4 @@
-package io.weaviate.client6.v1.data;
+package io.weaviate.client6.v1.collections.data;
import java.util.LinkedHashSet;
import java.util.List;
diff --git a/src/main/java/io/weaviate/client6/v1/collections/data/InsertObjectRequest.java b/src/main/java/io/weaviate/client6/v1/collections/data/InsertObjectRequest.java
new file mode 100644
index 000000000..a04f993ea
--- /dev/null
+++ b/src/main/java/io/weaviate/client6/v1/collections/data/InsertObjectRequest.java
@@ -0,0 +1,149 @@
+package io.weaviate.client6.v1.collections.data;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import io.weaviate.client6.v1.collections.Reference;
+import io.weaviate.client6.v1.collections.object.Vectors;
+
+public record InsertObjectRequest(String collection, T properties, String id, Vectors vectors,
+ Map> references) {
+
+ /** Create InsertObjectRequest from Builder options. */
+ public InsertObjectRequest(Builder builder) {
+ this(builder.collection, builder.properties, builder.id, builder.vectors, builder.references);
+ }
+
+ /**
+ * Construct InsertObjectRequest with optional parameters.
+ *
+ * @param Shape of the object properties, e.g.
+ * {@code Map}
+ * @param collection Collection to insert to.
+ * @param properties Object properties.
+ * @param fn Optional parameters
+ * @return InsertObjectRequest
+ */
+ static InsertObjectRequest of(String collection, T properties, Consumer> fn) {
+ var builder = new Builder<>(collection, properties);
+ fn.accept(builder);
+ return builder.build();
+ }
+
+ public static class Builder {
+ private final String collection; // Required
+ private final T properties; // Required
+
+ private String id;
+ private Vectors vectors;
+ private final Map> references = new HashMap<>();
+
+ Builder(String collection, T properties) {
+ this.collection = collection;
+ this.properties = properties;
+ }
+
+ /** Define custom object id. Must be a valid UUID. */
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Supply one or more (named) vectors. Calls to {@link #vectors} are not
+ * chainable. Use {@link Vectors#of(Consumer)} to pass multiple vectors.
+ */
+ public Builder vectors(Vectors vectors) {
+ this.vectors = vectors;
+ return this;
+ }
+
+ /**
+ * Add a reference. Calls to {@link #reference} can be chained
+ * to add multiple references.
+ */
+ public Builder reference(String property, Reference... references) {
+ for (var ref : references) {
+ addReference(property, ref);
+ }
+ return this;
+ }
+
+ private void addReference(String property, Reference reference) {
+ if (!references.containsKey(property)) {
+ references.put(property, new ArrayList<>());
+ }
+ references.get(property).add(reference);
+ }
+
+ /** Build a new InsertObjectRequest. */
+ public InsertObjectRequest build() {
+ return new InsertObjectRequest<>(this);
+ }
+ }
+
+ // Here we're just rawdogging JSON serialization just to get a good feel for it.
+ public String serialize(Gson gson) throws IOException {
+ var buf = new StringWriter();
+ var w = gson.newJsonWriter(buf);
+
+ w.beginObject();
+
+ w.name("class");
+ w.value(collection);
+
+ if (id != null) {
+ w.name("id");
+ w.value(id);
+ }
+
+ if (vectors != null) {
+ var unnamed = vectors.getUnnamed();
+ if (unnamed.isPresent()) {
+ w.name("vector");
+ gson.getAdapter(Float[].class).write(w, unnamed.get());
+ } else {
+ w.name("vectors");
+ gson.getAdapter(new TypeToken