diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index e7db1664b..9bc4b7fbc 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -13,7 +13,9 @@ import io.weaviate.client6.v1.api.collections.Property; import io.weaviate.client6.v1.api.collections.Vectors; import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.api.collections.data.Reference; import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.QueryReference; import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; import io.weaviate.client6.v1.api.collections.vectorizers.NoneVectorizer; import io.weaviate.containers.Container; @@ -101,4 +103,76 @@ private static void createTestCollections() throws IOException { Property.reference("hasAwards", awardsGrammy, awardsOscar)) .vectors(named -> named.vector(VECTOR_INDEX, Hnsw.of(new NoneVectorizer())))); } + + @Test + public void testReferences_AddReplaceDelete() throws IOException { + // Arrange + var nsPersons = ns("Person"); + + client.collections.create(nsPersons, + collection -> collection + .properties(Property.text("name")) + .references(Property.reference("hasFriend", nsPersons))); + + var persons = client.collections.use(nsPersons); + var john = persons.data.insert(Map.of("name", "john")); + var albie = persons.data.insert(Map.of("name", "albie")); + + // Act: add reference + persons.data.referenceAdd( + john.metadata().uuid(), + "hasFriend", + Reference.object(albie)); + + // Assert + var johnWithFriends = persons.query.byId(john.metadata().uuid(), + query -> query.returnReferences( + QueryReference.single("hasFriend", + friend -> friend.returnProperties("name")))); + + Assertions.assertThat(johnWithFriends).get() + .as("friends after ADD") + .extracting(WeaviateObject::references).extracting("hasFriend") + .asInstanceOf(InstanceOfAssertFactories.list(WeaviateObject.class)) + .hasSize(1) + .first().extracting(WeaviateObject::properties, InstanceOfAssertFactories.MAP) + .returns("albie", friend -> friend.get("name")); + + // Act: replace reference + var barbara = persons.data.insert(Map.of("name", "barbara")); + persons.data.referenceReplace( + john.metadata().uuid(), + "hasFriend", + Reference.object(barbara)); + + johnWithFriends = persons.query.byId(john.metadata().uuid(), + query -> query.returnReferences( + QueryReference.single("hasFriend", + friend -> friend.returnProperties("name")))); + + Assertions.assertThat(johnWithFriends).get() + .as("friends after REPLACE") + .extracting(WeaviateObject::references).extracting("hasFriend") + .asInstanceOf(InstanceOfAssertFactories.list(WeaviateObject.class)) + .hasSize(1) + .first().extracting(WeaviateObject::properties, InstanceOfAssertFactories.MAP) + .returns("barbara", friend -> friend.get("name")); + + // Act: delete reference + persons.data.referenceDelete( + john.metadata().uuid(), + "hasFriend", + Reference.object(barbara)); + + // Assert + johnWithFriends = persons.query.byId(john.metadata().uuid(), + query -> query.returnReferences( + QueryReference.single("hasFriend"))); + + Assertions.assertThat(johnWithFriends).get() + .as("friends after DELETE") + .extracting(WeaviateObject::references).extracting("hasFriend") + .asInstanceOf(InstanceOfAssertFactories.list(WeaviateObject.class)) + .isEmpty(); + } } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/Reference.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/Reference.java index 5cf0c6b68..b6cb75b33 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/data/Reference.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/Reference.java @@ -27,7 +27,12 @@ public static Reference uuids(String... uuids) { return new Reference(null, Arrays.asList(uuids)); } - /** Create references to {@link WeaviateObject}. */ + /** Create references to single {@link WeaviateObject}. */ + public static Reference object(WeaviateObject object) { + return new Reference(object.collection(), object.metadata().uuid()); + } + + /** Create references to multiple {@link WeaviateObject}. */ public static Reference[] objects(WeaviateObject... objects) { return Arrays.stream(objects) .map(o -> new Reference(o.collection(), o.metadata().uuid())) diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceAddRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceAddRequest.java new file mode 100644 index 000000000..834bc8e46 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceAddRequest.java @@ -0,0 +1,23 @@ +package io.weaviate.client6.v1.api.collections.data; + +import java.util.Collections; + +import org.apache.hc.core5.http.HttpStatus; + +import io.weaviate.client6.v1.internal.json.JSON; +import io.weaviate.client6.v1.internal.orm.CollectionDescriptor; +import io.weaviate.client6.v1.internal.rest.Endpoint; + +public record ReferenceAddRequest(String fromUuid, String fromProperty, Reference reference) { + + public static final Endpoint endpoint( + CollectionDescriptor descriptor) { + return Endpoint.of( + request -> "POST", + request -> "/objects/" + descriptor.name() + "/" + request.fromUuid + "/references/" + request.fromProperty, + (gson, request) -> JSON.serialize(request.reference), + request -> Collections.emptyMap(), + code -> code != HttpStatus.SC_SUCCESS, + (gson, response) -> null); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceDeleteRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceDeleteRequest.java new file mode 100644 index 000000000..b43e491db --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceDeleteRequest.java @@ -0,0 +1,23 @@ +package io.weaviate.client6.v1.api.collections.data; + +import java.util.Collections; + +import org.apache.hc.core5.http.HttpStatus; + +import io.weaviate.client6.v1.internal.json.JSON; +import io.weaviate.client6.v1.internal.orm.CollectionDescriptor; +import io.weaviate.client6.v1.internal.rest.Endpoint; + +public record ReferenceDeleteRequest(String fromUuid, String fromProperty, Reference reference) { + + public static final Endpoint endpoint( + CollectionDescriptor descriptor) { + return Endpoint.of( + request -> "DELETE", + request -> "/objects/" + descriptor.name() + "/" + request.fromUuid + "/references/" + request.fromProperty, + (gson, request) -> JSON.serialize(request.reference), + request -> Collections.emptyMap(), + code -> code != HttpStatus.SC_SUCCESS, + (gson, response) -> null); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceReplaceRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceReplaceRequest.java new file mode 100644 index 000000000..bf148499c --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/ReferenceReplaceRequest.java @@ -0,0 +1,24 @@ +package io.weaviate.client6.v1.api.collections.data; + +import java.util.Collections; +import java.util.List; + +import org.apache.hc.core5.http.HttpStatus; + +import io.weaviate.client6.v1.internal.json.JSON; +import io.weaviate.client6.v1.internal.orm.CollectionDescriptor; +import io.weaviate.client6.v1.internal.rest.Endpoint; + +public record ReferenceReplaceRequest(String fromUuid, String fromProperty, Reference reference) { + + public static final Endpoint endpoint( + CollectionDescriptor descriptor) { + return Endpoint.of( + request -> "PUT", + request -> "/objects/" + descriptor.name() + "/" + request.fromUuid + "/references/" + request.fromProperty, + (gson, request) -> JSON.serialize(List.of(request.reference)), + request -> Collections.emptyMap(), + code -> code != HttpStatus.SC_SUCCESS, + (gson, response) -> null); + } +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/WeaviateDataClient.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/WeaviateDataClient.java index b4466b72d..e08cd0ee6 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/data/WeaviateDataClient.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/WeaviateDataClient.java @@ -36,4 +36,28 @@ public void delete(String uuid) throws IOException { this.restTransport.performRequest(new DeleteObjectRequest(collectionDescriptor.name(), uuid), DeleteObjectRequest._ENDPOINT); } + + public void referenceAdd(String fromUuid, String fromProperty, Reference reference) throws IOException { + for (var uuid : reference.uuids()) { + var singleRef = new Reference(reference.collection(), uuid); + this.restTransport.performRequest(new ReferenceAddRequest(fromUuid, fromProperty, singleRef), + ReferenceAddRequest.endpoint(collectionDescriptor)); + } + } + + public void referenceDelete(String fromUuid, String fromProperty, Reference reference) throws IOException { + for (var uuid : reference.uuids()) { + var singleRef = new Reference(reference.collection(), uuid); + this.restTransport.performRequest(new ReferenceDeleteRequest(fromUuid, fromProperty, singleRef), + ReferenceDeleteRequest.endpoint(collectionDescriptor)); + } + } + + public void referenceReplace(String fromUuid, String fromProperty, Reference reference) throws IOException { + for (var uuid : reference.uuids()) { + var singleRef = new Reference(reference.collection(), uuid); + this.restTransport.performRequest(new ReferenceReplaceRequest(fromUuid, fromProperty, singleRef), + ReferenceReplaceRequest.endpoint(collectionDescriptor)); + } + } }