From 132ba34d4d5143ddf9f7f2e6eb6d6ad4b629dac5 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sat, 27 Sep 2025 01:01:45 +0200 Subject: [PATCH 1/4] fix: handle missing primitive values in ORM --- .../io/weaviate/integration/ORMITest.java | 47 ++++++++++++++++++- .../client6/v1/internal/orm/PojoBuilder.java | 22 +++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/it/java/io/weaviate/integration/ORMITest.java b/src/it/java/io/weaviate/integration/ORMITest.java index d46d1600c..16b5b3717 100644 --- a/src/it/java/io/weaviate/integration/ORMITest.java +++ b/src/it/java/io/weaviate/integration/ORMITest.java @@ -1,5 +1,6 @@ package io.weaviate.integration; +import java.io.IOException; import java.time.OffsetDateTime; import java.util.List; import java.util.Map; @@ -14,6 +15,7 @@ import io.weaviate.ConcurrentTest; import io.weaviate.client6.v1.api.WeaviateClient; import io.weaviate.client6.v1.api.collections.CollectionConfig; +import io.weaviate.client6.v1.api.collections.WeaviateObject; import io.weaviate.client6.v1.api.collections.annotations.Collection; import io.weaviate.client6.v1.api.collections.annotations.Property; import io.weaviate.client6.v1.api.collections.data.InsertManyResponse.InsertObject; @@ -23,7 +25,7 @@ public class ORMITest extends ConcurrentTest { private static WeaviateClient client = Container.WEAVIATE.getClient(); - @Collection("ORMITest") + @Collection("ORMITestThings") static record Thing( // text / text[] String text, @@ -95,7 +97,7 @@ public void test_createCollection() throws Exception { // Assert Assertions.assertThat(config).get() - .returns("ORMITest", CollectionConfig::collectionName) + .returns("ORMITestThings", CollectionConfig::collectionName) .extracting(CollectionConfig::properties, InstanceOfAssertFactories.list(io.weaviate.client6.v1.api.collections.Property.class)) .extracting(p -> Map.entry( @@ -307,4 +309,45 @@ public void test_insertManyAndQuery() throws Exception { .usingRecursiveComparison(COMPARISON_CONFIG) .asInstanceOf(InstanceOfAssertFactories.list(Thing.class)); } + + @Collection("ORMITestSongs") + record Song( + String title, + String album, + int year, + boolean hasAward, + Long monthlyListeners) { + } + + /** + * Test that serialization works correctly when some fields are null and + * deserialization works correctly when some properties are not returned. + */ + @Test + public void test_partialScan() throws IOException { + client.collections.create(Song.class); + + var songs = client.collections.use(Song.class); + + // Act: insert with nulls + var dystopia = songs.data.insert(new Song( + "Dystopia", + null, + 2016, + true, + null)); + + // Act: return subset of the properties + var got = songs.query.byId(dystopia.uuid(), + q -> q.returnProperties("title", "hasAward")); + + // Assert + Assertions.assertThat(got).get() + .extracting(WeaviateObject::properties) + .returns("Dystopia", Song::title) + .returns(null, Song::album) + .returns(0, Song::year) + .returns(true, Song::hasAward) + .returns(null, Song::monthlyListeners); + } } diff --git a/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java b/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java index 1fa65750c..d94f772a8 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java +++ b/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java @@ -19,6 +19,28 @@ static record Arg(Class type, Object value) { Arg withValue(Object value) { return new Arg(this.type, value); } + + public Object value() { + if (value != null) { + return value; + } + + if (type == boolean.class) { + return false; + } else if (type == short.class) { + return (short) 0; + } else if (type == int.class) { + return 0; + } else if (type == long.class) { + return 0L; + } else if (type == float.class) { + return 0f; + } else if (type == double.class) { + return 0d; + } + + throw new IllegalArgumentException(type.getName() + " property data type is not supported"); + } } PojoBuilder(PojoDescriptor descriptor) { From 76d5c0eff4c24bd6692fbf727011396b1c48ee35 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sat, 27 Sep 2025 01:03:33 +0200 Subject: [PATCH 2/4] refactor: rename setInteger -> setLong Consistent with how setDouble() accepts a Double even though the Weaviate's data type is number. PropertyBuilder deals with Java data types so the methods should be named accordingly. --- .../client6/v1/api/collections/query/QueryRequest.java | 2 +- .../java/io/weaviate/client6/v1/internal/orm/MapBuilder.java | 2 +- .../java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java | 3 +-- .../io/weaviate/client6/v1/internal/orm/PropertiesBuilder.java | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/QueryRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/QueryRequest.java index 4cd096386..fa07d67d5 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/QueryRequest.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/QueryRequest.java @@ -221,7 +221,7 @@ private static void setProperty(String property, WeaviateProtoProperties.Val } else if (value.hasBoolValue()) { builder.setBoolean(property, value.getBoolValue()); } else if (value.hasIntValue()) { - builder.setInteger(property, value.getIntValue()); + builder.setLong(property, value.getIntValue()); } else if (value.hasNumberValue()) { builder.setDouble(property, value.getNumberValue()); } else if (value.hasBlobValue()) { diff --git a/src/main/java/io/weaviate/client6/v1/internal/orm/MapBuilder.java b/src/main/java/io/weaviate/client6/v1/internal/orm/MapBuilder.java index 0e2c94c99..6d1383160 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/orm/MapBuilder.java +++ b/src/main/java/io/weaviate/client6/v1/internal/orm/MapBuilder.java @@ -25,7 +25,7 @@ public void setBoolean(String property, Boolean value) { } @Override - public void setInteger(String property, Long value) { + public void setLong(String property, Long value) { properties.put(property, value); } diff --git a/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java b/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java index d94f772a8..9583dca98 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java +++ b/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java @@ -125,8 +125,7 @@ public void setBoolean(String propertyName, Boolean value) { } @Override - // TODO: rename to setLong - public void setInteger(String propertyName, Long value) { + public void setLong(String propertyName, Long value) { if (isType(propertyName, short.class, Short.class)) { setValue(propertyName, value.shortValue()); } else if (isType(propertyName, int.class, Integer.class)) { diff --git a/src/main/java/io/weaviate/client6/v1/internal/orm/PropertiesBuilder.java b/src/main/java/io/weaviate/client6/v1/internal/orm/PropertiesBuilder.java index dd46f87c9..797831640 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/orm/PropertiesBuilder.java +++ b/src/main/java/io/weaviate/client6/v1/internal/orm/PropertiesBuilder.java @@ -11,7 +11,7 @@ public interface PropertiesBuilder { void setBoolean(String property, Boolean value); - void setInteger(String property, Long value); + void setLong(String property, Long value); void setDouble(String property, Double value); From 09b7bf5c8749d340a0eaeb7c79ab19c2ea61bdf2 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sat, 27 Sep 2025 01:04:41 +0200 Subject: [PATCH 3/4] chore: move logo to ./logo.png --- README.md | 6 +++--- assets/duke-client6.png => logo.png | Bin 2 files changed, 3 insertions(+), 3 deletions(-) rename assets/duke-client6.png => logo.png (100%) diff --git a/README.md b/README.md index 8ac7a8115..4310d285d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Weaviate Java client Weaviate logo +# Weaviate Java client Weaviate logo [![Build Status](https://github.com/weaviate/java-client/actions/workflows/.github/workflows/test.yaml/badge.svg?branch=main)](https://github.com/weaviate/java-client/actions/workflows/.github/workflows/test.yaml) @@ -183,7 +183,7 @@ WeaviateClient wcd = WeaviateClient.connectToWeaviateCloud("my-cluster-url.io", > [!TIP] > The client holds a number of resources (HTTP connection pools, gRPC channel) which must be disposed of correclty then they are no longer needed. > If the client's lifecycle is tied to that of your app, closing the client via `client.close()` is a good way to do that. -> +> > Otherwise, use the client inside a [try-with-resources](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) statement: > >```java @@ -684,7 +684,7 @@ var song1 = songs.query.byId( song -> song.returnReferences(QueryReference.single("artist")) ); System.out.println( - "Artist's last name is: " + + "Artist's last name is: " + song1.properties().artist().lastName() ); ``` diff --git a/assets/duke-client6.png b/logo.png similarity index 100% rename from assets/duke-client6.png rename to logo.png From fb03bd1a44fe5480c5bcc26b6b4535eef33638d1 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Sat, 27 Sep 2025 01:22:25 +0200 Subject: [PATCH 4/4] fix: do not throw IllegalArgumentException --- .../client6/v1/internal/orm/PojoBuilder.java | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java b/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java index 9583dca98..223fd0f53 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java +++ b/src/main/java/io/weaviate/client6/v1/internal/orm/PojoBuilder.java @@ -11,35 +11,36 @@ import org.apache.commons.lang3.ArrayUtils; final class PojoBuilder implements PropertiesBuilder { + private static final Map, Object> PRIMITIVE_DEFAULTS; + + static { + PRIMITIVE_DEFAULTS = Map.of( + boolean.class, false, + short.class, (short) 0, + int.class, 0, + long.class, 0L, + float.class, 0f, + double.class, 0d); + } + private final PojoDescriptor descriptor; private final Constructor ctor; private final Map ctorArgs; static record Arg(Class type, Object value) { - Arg withValue(Object value) { - return new Arg(this.type, value); - } - - public Object value() { - if (value != null) { - return value; - } - - if (type == boolean.class) { - return false; - } else if (type == short.class) { - return (short) 0; - } else if (type == int.class) { - return 0; - } else if (type == long.class) { - return 0L; - } else if (type == float.class) { - return 0f; - } else if (type == double.class) { - return 0d; + /** + * Create a new Arg, replacing a null value with + * default if the type is a known primitive class. + */ + static Arg withPrimitiveDefault(Class type, Object value) { + if (PRIMITIVE_DEFAULTS.containsKey(type)) { + return new Arg(type, PRIMITIVE_DEFAULTS.get(type)); } + return new Arg(type, value); + } - throw new IllegalArgumentException(type.getName() + " property data type is not supported"); + Arg withValue(Object value) { + return new Arg(this.type, value); } } @@ -53,7 +54,7 @@ public Object value() { .map(arg -> { // LinkedHashMap allows null values. var type = arg.getType(); - ctorArgs.put(arg.getName(), new Arg(type, null)); + ctorArgs.put(arg.getName(), Arg.withPrimitiveDefault(type, null)); return type; }) .toArray(Class[]::new);