From 65b589f5891776aa8116aa10910b67dd6bf72f5b Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 7 Oct 2025 12:42:59 +0200 Subject: [PATCH 1/9] feat: support creating, inserting, and reading 'object' properties --- .../java/io/weaviate/containers/Weaviate.java | 2 +- .../integration/CollectionsITest.java | 23 ++++++ .../io/weaviate/integration/DataITest.java | 16 ++-- .../client6/v1/api/collections/DataType.java | 4 +- .../client6/v1/api/collections/Property.java | 81 +++++++++++++++++-- .../api/collections/query/QueryRequest.java | 12 ++- .../client6/v1/internal/orm/MapBuilder.java | 5 ++ .../client6/v1/internal/orm/PojoBuilder.java | 5 ++ .../v1/internal/orm/PojoDescriptor.java | 2 + .../v1/internal/orm/PropertiesBuilder.java | 2 + 10 files changed, 136 insertions(+), 16 deletions(-) diff --git a/src/it/java/io/weaviate/containers/Weaviate.java b/src/it/java/io/weaviate/containers/Weaviate.java index 88cd23ee0..661273d74 100644 --- a/src/it/java/io/weaviate/containers/Weaviate.java +++ b/src/it/java/io/weaviate/containers/Weaviate.java @@ -15,7 +15,7 @@ import io.weaviate.client6.v1.internal.ObjectBuilder; public class Weaviate extends WeaviateContainer { - public static final String VERSION = "1.32.3"; + public static final String VERSION = "1.33.0"; public static final String DOCKER_IMAGE = "semitechnologies/weaviate"; private volatile SharedClient clientInstance; diff --git a/src/it/java/io/weaviate/integration/CollectionsITest.java b/src/it/java/io/weaviate/integration/CollectionsITest.java index 7e21784e6..f37c1eb72 100644 --- a/src/it/java/io/weaviate/integration/CollectionsITest.java +++ b/src/it/java/io/weaviate/integration/CollectionsITest.java @@ -10,6 +10,7 @@ import io.weaviate.client6.v1.api.WeaviateApiException; import io.weaviate.client6.v1.api.WeaviateClient; import io.weaviate.client6.v1.api.collections.CollectionConfig; +import io.weaviate.client6.v1.api.collections.DataType; import io.weaviate.client6.v1.api.collections.InvertedIndex; import io.weaviate.client6.v1.api.collections.Property; import io.weaviate.client6.v1.api.collections.ReferenceProperty; @@ -191,4 +192,26 @@ public void testShards() throws IOException { public void testInvalidCollectionName() throws IOException { client.collections.create("^collection@weaviate.io$"); } + + @Test + public void testNestedProperties() throws IOException, Exception { + var nsBuildings = "Buildings"; + + client.collections.create( + nsBuildings, c -> c.properties( + Property.object("address", p -> p.nestedProperties( + Property.text("street"), + Property.integer("buildingNr"), + Property.bool("isOneWay"))))); + + var config = client.collections.getConfig(nsBuildings); + + Assertions.assertThat(config).get() + .extracting(CollectionConfig::properties, InstanceOfAssertFactories.list(Property.class)) + .hasSize(1).first() + .returns("address", Property::propertyName) + .extracting(Property::nestedProperties, InstanceOfAssertFactories.list(Property.class)) + .extracting(Property::dataTypes).extracting(types -> types.get(0)) + .containsExactly(DataType.TEXT, DataType.INT, DataType.BOOL); + } } diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index c679f7a7a..7122c63cf 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -431,7 +431,10 @@ public void testDataTypes() throws IOException { Property.boolArray("prop_bool_array"), Property.dateArray("prop_date_array"), Property.uuidArray("prop_uuid_array"), - Property.textArray("prop_text_array"))); + Property.textArray("prop_text_array"), + Property.object("prop_object", + p -> p.nestedProperties( + Property.text("marco"))))); var types = client.collections.use(nsDataTypes); @@ -450,13 +453,12 @@ public void testDataTypes() throws IOException { Map.entry("prop_bool_array", List.of(true, false)), Map.entry("prop_date_array", List.of(now, now)), Map.entry("prop_uuid_array", List.of(uuid, uuid)), - Map.entry("prop_text_array", List.of("a", "b", "c"))); - var returnProperties = want.keySet().toArray(String[]::new); + Map.entry("prop_text_array", List.of("a", "b", "c")), + Map.entry("prop_object", Map.of("marco", "polo"))); // Act var object = types.data.insert(want); - var got = types.query.byId(object.uuid(), - q -> q.returnProperties(returnProperties)); + var got = types.query.byId(object.uuid()); // return all properties // Assert Assertions.assertThat(got).get() @@ -465,4 +467,8 @@ public void testDataTypes() throws IOException { .containsAllEntriesOf(want); } + + @Test + public void testNestedProperties() throws IOException { + } } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/DataType.java b/src/main/java/io/weaviate/client6/v1/api/collections/DataType.java index 91584a797..7b99e8470 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/DataType.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/DataType.java @@ -18,6 +18,8 @@ public interface DataType { public static final String DATE_ARRAY = "date[]"; public static final String UUID = "uuid"; public static final String UUID_ARRAY = "uuid[]"; + public static final String OBJECT = "object"; + public static final String OBJECT_ARRAY = "object[]"; /** * Scalar/array types which Weaviate and WeaviateClient recognize. @@ -31,6 +33,6 @@ public interface DataType { * using {@link Property}'s factory classes. */ public static final Set KNOWN_TYPES = ImmutableSet.of( - TEXT, INT, BLOB, BOOL, DATE, UUID, NUMBER, + TEXT, INT, BLOB, BOOL, DATE, UUID, NUMBER, OBJECT, TEXT_ARRAY, INT_ARRAY, NUMBER_ARRAY, BOOL_ARRAY, DATE_ARRAY, UUID_ARRAY); } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/Property.java b/src/main/java/io/weaviate/client6/v1/api/collections/Property.java index 66121675c..f3e69cf3a 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/Property.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/Property.java @@ -1,5 +1,7 @@ package io.weaviate.client6.v1.api.collections; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -17,7 +19,8 @@ public record Property( @SerializedName("indexSearchable") Boolean indexSearchable, @SerializedName("tokenization") Tokenization tokenization, @SerializedName("skipVectorization") Boolean skipVectorization, - @SerializedName("vectorizePropertyName") Boolean vectorizePropertyName) { + @SerializedName("vectorizePropertyName") Boolean vectorizePropertyName, + @SerializedName("nestedProperties") List nestedProperties) { /** * Create a {@code text} property. @@ -96,7 +99,7 @@ public static Property integerArray(String name, Function> fn) { + return newProperty(name, DataType.OBJECT, fn); + } + + /** + * Create a {@code object[]} property. + * + * @param name Property name. + */ + public static Property objectArray(String name) { + return objectArray(name, ObjectBuilder.identity()); + } + + /** + * Create a {@code objectArray[]} property with additional configuration. + * + * @param name Property name. + * @param fn Lambda expression for optional parameters. + */ + public static Property objectArray(String name, Function> fn) { + return newProperty(name, DataType.OBJECT_ARRAY, fn); + } + private static Property newProperty(String name, String dataType, Function> fn) { return fn.apply(new Builder(name, dataType)).build(); } @@ -329,7 +370,8 @@ public Property(Builder builder) { builder.indexSearchable, builder.tokenization, builder.skipVectorization, - builder.vectorizePropertyName); + builder.vectorizePropertyName, + builder.nestedProperties.isEmpty() ? null : builder.nestedProperties); } // All methods accepting a `boolean` should have a boxed overload @@ -346,9 +388,9 @@ public Property(Builder builder) { public static class Builder implements ObjectBuilder { // Required parameters. private final String propertyName; + private final List dataTypes = new ArrayList<>(); // Optional parameters. - private List dataTypes; private String description; private Boolean indexInverted; private Boolean indexFilterable; @@ -357,6 +399,7 @@ public static class Builder implements ObjectBuilder { private Tokenization tokenization; private Boolean skipVectorization; private Boolean vectorizePropertyName; + private List nestedProperties = new ArrayList<>(); /** * Create a scalar / array type property. @@ -365,7 +408,7 @@ public static class Builder implements ObjectBuilder { */ public Builder(String propertyName, String dataType) { this.propertyName = propertyName; - this.dataTypes = List.of(dataType); + this.dataTypes.add(dataType); } /** @@ -375,7 +418,7 @@ public Builder(String propertyName, String dataType) { */ public Builder(String propertyName, List dataTypes) { this.propertyName = propertyName; - this.dataTypes = List.copyOf(dataTypes); + this.dataTypes.addAll(dataTypes); } /** Add property description. */ @@ -491,6 +534,32 @@ public Builder vectorizePropertyName(boolean vectorizePropertyName) { return this; } + /** + * Defined nested properties. This configuration is only applicable to a + * property of type {@code object} and {@code object[]}. + * + *
{@code
+     * Property.object("address",
+     *     p -> p.nestedProperties(
+     *         Property.text("street"),
+     *         Property.integer("building_nr")))
+     * }
+ */ + public Builder nestedProperties(Property... properties) { + return nestedProperties(Arrays.asList(properties)); + } + + /** + * Defined nested properties. This configuration is only applicable to a + * property of type {@code object} and {@code object[]}. + * + * @see Builder#nestedProperties(Property...) + */ + public Builder nestedProperties(List properties) { + this.nestedProperties.addAll(properties); + return this; + } + /** Convenience method to be used by {@link Property#edit}. */ Builder vectorizePropertyName(Boolean vectorizePropertyName) { this.vectorizePropertyName = vectorizePropertyName; 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 fa07d67d5..fd5908b76 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 @@ -136,8 +136,8 @@ private static WeaviateObject unmarshalWithRefere WeaviateProtoSearchGet.MetadataResult metadataResult, CollectionDescriptor descriptor) { var properties = descriptor.propertiesBuilder(); - propertiesResult.getNonRefProps().getFieldsMap() - .entrySet().stream().forEach(entry -> setProperty(entry.getKey(), entry.getValue(), properties)); + propertiesResult.getNonRefProps().getFieldsMap().entrySet().stream() + .forEach(entry -> setProperty(entry.getKey(), entry.getValue(), properties, descriptor)); // In case a reference is multi-target, there will be a separate // "reference property" for each of the targets, so instead of @@ -213,7 +213,7 @@ private static WeaviateObject unmarshalWithRefere } private static void setProperty(String property, WeaviateProtoProperties.Value value, - PropertiesBuilder builder) { + PropertiesBuilder builder, CollectionDescriptor descriptor) { if (value.hasNullValue()) { builder.setNull(property); } else if (value.hasTextValue()) { @@ -255,6 +255,12 @@ private static void setProperty(String property, WeaviateProtoProperties.Val .map(DateUtil::fromISO8601).toList(); builder.setOffsetDateTimeArray(property, dates); } + } else if (value.hasObjectValue()) { + var object = value.getObjectValue(); + var properties = descriptor.propertiesBuilder(); + object.getFieldsMap().entrySet().stream() + .forEach(entry -> setProperty(entry.getKey(), entry.getValue(), properties, descriptor)); + builder.setNestedObject(property, properties.build()); } else { throw new IllegalArgumentException(property + " data type is not supported"); } 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 6d1383160..16d8219bd 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 @@ -79,6 +79,11 @@ public void setOffsetDateTimeArray(String property, List value) properties.put(property, value); } + @Override + public void setNestedObject(String property, Object value) { + properties.put(property, value); + } + @Override public Map build() { return properties; 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 223fd0f53..da9a56309 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 @@ -227,6 +227,11 @@ public void setOffsetDateTimeArray(String propertyName, List val : value); } + @Override + public void setNestedObject(String propertyName, Object value) { + setValue(propertyName, value); + } + @Override public PropertiesT build() { Object[] args = ctorArgs.values().stream().map(Arg::value).toArray(); diff --git a/src/main/java/io/weaviate/client6/v1/internal/orm/PojoDescriptor.java b/src/main/java/io/weaviate/client6/v1/internal/orm/PojoDescriptor.java index d0a1f218b..0b5724153 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/orm/PojoDescriptor.java +++ b/src/main/java/io/weaviate/client6/v1/internal/orm/PojoDescriptor.java @@ -63,6 +63,8 @@ final class PojoDescriptor implements CollectionDescriptor put(Float[].class, Property::numberArray); put(double[].class, Property::numberArray); put(Double[].class, Property::numberArray); + + put(Map.class, Property::object); } }; CTORS = Collections.unmodifiableMap(ctors); 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 797831640..97445d431 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 @@ -33,5 +33,7 @@ public interface PropertiesBuilder { void setOffsetDateTimeArray(String property, List value); + void setNestedObject(String property, Object value); + T build(); } From df8b0c7724b7bed0e2aea1fd0e6a069216fb26b6 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 7 Oct 2025 13:22:25 +0200 Subject: [PATCH 2/9] feat: support batch inserting nested objects --- .../io/weaviate/integration/DataITest.java | 28 ++- .../collections/data/InsertManyRequest.java | 230 ++++++++++-------- 2 files changed, 155 insertions(+), 103 deletions(-) diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index 7122c63cf..7762d8a00 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -469,6 +469,32 @@ public void testDataTypes() throws IOException { } @Test - public void testNestedProperties() throws IOException { + public void testNestedProperties_insertMany() throws IOException { + // Arrange + var nsBuildings = "Buildings"; + + client.collections.create( + nsBuildings, c -> c.properties( + Property.object("address", p -> p.nestedProperties( + Property.text("street"), + Property.integer("buildingNr"), + Property.bool("isOneWay"))))); + + var buildings = client.collections.use("nsBuildings"); + + Map house_1 = Map.of("address", Map.of( + "street", "Burggasse", + "building_nr", 51, + "isOneWay", true)); + Map house_2 = Map.of("address", Map.of( + "street", "Port Mariland St.", + "building_nr", 111, + "isOneWay", false)); + + // Act + var result = buildings.data.insertMany(house_1, house_2); + + // Assert + Assertions.assertThat(result.errors()).isEmpty(); } } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java index b4d86cfbc..3ff4399a2 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java @@ -4,11 +4,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.UUID; import io.weaviate.client6.v1.api.collections.CollectionHandleDefaults; import io.weaviate.client6.v1.api.collections.ObjectMetadata; import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.internal.Debug; import io.weaviate.client6.v1.internal.MapUtil; import io.weaviate.client6.v1.internal.grpc.ByteStringUtil; import io.weaviate.client6.v1.internal.grpc.Rpc; @@ -136,110 +138,15 @@ public static void buildObject(WeaviateProtoBatch.BatchObject.Builder object collection .propertiesReader(insert.properties()).readProperties() .entrySet().stream().forEach(entry -> { - var value = entry.getValue(); - var protoValue = com.google.protobuf.Value.newBuilder(); - - if (value == null) { - return; - } - - if (value instanceof String v) { - protoValue.setStringValue(v); - } else if (value instanceof UUID v) { - protoValue.setStringValue(v.toString()); - } else if (value instanceof OffsetDateTime v) { - protoValue.setStringValue(v.toString()); - } else if (value instanceof Boolean v) { - protoValue.setBoolValue(v.booleanValue()); - } else if (value instanceof Number v) { - protoValue.setNumberValue(v.doubleValue()); - } else if (value instanceof List v) { - protoValue.setListValue( - com.google.protobuf.ListValue.newBuilder() - .addAllValues(v.stream() - .map(listValue -> { - var protoListValue = com.google.protobuf.Value.newBuilder(); - if (listValue instanceof String lv) { - protoListValue.setStringValue(lv); - } else if (listValue instanceof UUID lv) { - protoListValue.setStringValue(lv.toString()); - } else if (listValue instanceof OffsetDateTime lv) { - protoListValue.setStringValue(lv.toString()); - } else if (listValue instanceof Boolean lv) { - protoListValue.setBoolValue(lv); - } else if (listValue instanceof Number lv) { - protoListValue.setNumberValue(lv.doubleValue()); - } - return protoListValue.build(); - }) - .toList())); - - } else if (value.getClass().isArray()) { - List values; - - if (value instanceof String[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setStringValue(lv).build()).toList(); - } else if (value instanceof UUID[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setStringValue(lv.toString()).build()).toList(); - } else if (value instanceof OffsetDateTime[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setStringValue(lv.toString()).build()).toList(); - } else if (value instanceof Boolean[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setBoolValue(lv).build()).toList(); - } else if (value instanceof boolean[] v) { - values = new ArrayList<>(); - for (boolean b : v) { - values.add(com.google.protobuf.Value.newBuilder().setBoolValue(b).build()); - } - } else if (value instanceof short[] v) { - values = new ArrayList<>(); - for (short s : v) { - values.add(com.google.protobuf.Value.newBuilder().setNumberValue(s).build()); - } - } else if (value instanceof int[] v) { - values = Arrays.stream(v).boxed() - .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); - } else if (value instanceof long[] v) { - values = Arrays.stream(v).boxed() - .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); - } else if (value instanceof float[] v) { - values = new ArrayList<>(); - for (float s : v) { - values.add(com.google.protobuf.Value.newBuilder().setNumberValue(s).build()); - } - } else if (value instanceof double[] v) { - values = Arrays.stream(v).boxed() - .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); - } else if (value instanceof Short[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); - } else if (value instanceof Integer[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); - } else if (value instanceof Long[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); - } else if (value instanceof Float[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); - } else if (value instanceof Double[] v) { - values = Arrays.stream(v) - .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); - } else { - throw new AssertionError("(insertMany) branch not covered " + value.getClass()); + try { + var protoValue = marshalValue(entry.getValue()); + if (protoValue == null) { + return; } - - protoValue.setListValue(com.google.protobuf.ListValue.newBuilder() - .addAllValues(values) - .build()); - } else { - throw new AssertionError("(insertMany) branch not covered " + value.getClass()); + nonRef.putFields(entry.getKey(), protoValue); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("marshal property " + entry.getKey(), e); } - - nonRef.putFields(entry.getKey(), protoValue.build()); }); insert.references() @@ -272,5 +179,124 @@ public static void buildObject(WeaviateProtoBatch.BatchObject.Builder object .addAllMultiTargetRefProps(multiRef); object.setProperties(properties); + + Debug.printProto(object); + } + + private static com.google.protobuf.Value marshalValue(Object value) { + var protoValue = com.google.protobuf.Value.newBuilder(); + + if (value == null) { + return null; + } + + if (value instanceof String v) { + protoValue.setStringValue(v); + } else if (value instanceof UUID v) { + protoValue.setStringValue(v.toString()); + } else if (value instanceof OffsetDateTime v) { + protoValue.setStringValue(v.toString()); + } else if (value instanceof Boolean v) { + protoValue.setBoolValue(v.booleanValue()); + } else if (value instanceof Number v) { + protoValue.setNumberValue(v.doubleValue()); + } else if (value instanceof List v) { + protoValue.setListValue( + com.google.protobuf.ListValue.newBuilder() + .addAllValues(v.stream() + .map(listValue -> { + var protoListValue = com.google.protobuf.Value.newBuilder(); + if (listValue instanceof String lv) { + protoListValue.setStringValue(lv); + } else if (listValue instanceof UUID lv) { + protoListValue.setStringValue(lv.toString()); + } else if (listValue instanceof OffsetDateTime lv) { + protoListValue.setStringValue(lv.toString()); + } else if (listValue instanceof Boolean lv) { + protoListValue.setBoolValue(lv); + } else if (listValue instanceof Number lv) { + protoListValue.setNumberValue(lv.doubleValue()); + } + return protoListValue.build(); + }) + .toList())); + + } else if (value.getClass().isArray()) { + List values; + + if (value instanceof String[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setStringValue(lv).build()).toList(); + } else if (value instanceof UUID[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setStringValue(lv.toString()).build()).toList(); + } else if (value instanceof OffsetDateTime[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setStringValue(lv.toString()).build()).toList(); + } else if (value instanceof Boolean[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setBoolValue(lv).build()).toList(); + } else if (value instanceof boolean[] v) { + values = new ArrayList<>(); + for (boolean b : v) { + values.add(com.google.protobuf.Value.newBuilder().setBoolValue(b).build()); + } + } else if (value instanceof short[] v) { + values = new ArrayList<>(); + for (short s : v) { + values.add(com.google.protobuf.Value.newBuilder().setNumberValue(s).build()); + } + } else if (value instanceof int[] v) { + values = Arrays.stream(v).boxed() + .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else if (value instanceof long[] v) { + values = Arrays.stream(v).boxed() + .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else if (value instanceof float[] v) { + values = new ArrayList<>(); + for (float s : v) { + values.add(com.google.protobuf.Value.newBuilder().setNumberValue(s).build()); + } + } else if (value instanceof double[] v) { + values = Arrays.stream(v).boxed() + .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else if (value instanceof Short[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else if (value instanceof Integer[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else if (value instanceof Long[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else if (value instanceof Float[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else if (value instanceof Double[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else { + throw new IllegalArgumentException("array type " + value.getClass() + " is not supported"); + } + + protoValue.setListValue(com.google.protobuf.ListValue.newBuilder() + .addAllValues(values) + .build()); + } else if (value instanceof Map v) { + var protoNested = com.google.protobuf.Struct.newBuilder(); + v.entrySet().stream() + .forEach(nested -> { + var nestedValue = marshalValue(nested.getValue()); + if (nestedValue == null) { + return; + } + protoNested.putFields((String) nested.getKey(), nestedValue); + }); + protoValue.setStructValue(protoNested); + } else { + throw new IllegalArgumentException("data type " + value.getClass() + " is not supported"); + } + + return protoValue.build(); } } From aba509414aad06b276e811ff2b5f778db14d5314 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 7 Oct 2025 15:56:10 +0200 Subject: [PATCH 3/9] feat: support writing records as nested properties --- src/it/java/io/weaviate/integration/DataITest.java | 14 ++++++++++---- .../v1/api/collections/data/InsertManyRequest.java | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index 7762d8a00..fee4f07ef 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -468,6 +468,12 @@ public void testDataTypes() throws IOException { } + record Address( + String street, + @io.weaviate.client6.v1.api.collections.annotations.Property("building_nr") int buildingNr, + @io.weaviate.client6.v1.api.collections.annotations.Property("isOneWay") boolean oneWay) { + } + @Test public void testNestedProperties_insertMany() throws IOException { // Arrange @@ -486,10 +492,10 @@ public void testNestedProperties_insertMany() throws IOException { "street", "Burggasse", "building_nr", 51, "isOneWay", true)); - Map house_2 = Map.of("address", Map.of( - "street", "Port Mariland St.", - "building_nr", 111, - "isOneWay", false)); + Map house_2 = Map.of("address", new Address( + "Port Mariland St.", + 111, + false)); // Act var result = buildings.data.insertMany(house_1, house_2); diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java index 3ff4399a2..0f24cff35 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java @@ -293,6 +293,20 @@ private static com.google.protobuf.Value marshalValue(Object value) { protoNested.putFields((String) nested.getKey(), nestedValue); }); protoValue.setStructValue(protoNested); + } else if (value instanceof Record r) { + @SuppressWarnings("unchecked") + CollectionDescriptor recordDescriptor = (CollectionDescriptor) CollectionDescriptor + .ofClass(r.getClass()); + var protoRecord = com.google.protobuf.Struct.newBuilder(); + recordDescriptor.propertiesReader(r).readProperties().entrySet().stream() + .forEach(recordField -> { + var nestedValue = marshalValue(recordField.getValue()); + if (nestedValue == null) { + return; + } + protoRecord.putFields((String) recordField.getKey(), nestedValue); + }); + protoValue.setStructValue(protoRecord); } else { throw new IllegalArgumentException("data type " + value.getClass() + " is not supported"); } From b61086f73b719762d30483b80bf36c341fcc8a0d Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 7 Oct 2025 16:36:40 +0200 Subject: [PATCH 4/9] refactor: factor out marshaling a Struct This is done 3 times: 1. High-level properties 2. Map 3. Custom records --- .../io/weaviate/integration/DataITest.java | 2 +- .../collections/data/InsertManyRequest.java | 63 +++++++------------ 2 files changed, 22 insertions(+), 43 deletions(-) diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index fee4f07ef..abb627c47 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -486,7 +486,7 @@ public void testNestedProperties_insertMany() throws IOException { Property.integer("buildingNr"), Property.bool("isOneWay"))))); - var buildings = client.collections.use("nsBuildings"); + var buildings = client.collections.use(nsBuildings); Map house_1 = Map.of("address", Map.of( "street", "Burggasse", diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java index 0f24cff35..fff3ba479 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java @@ -130,25 +130,9 @@ public static void buildObject(WeaviateProtoBatch.BatchObject.Builder object } } - var properties = WeaviateProtoBatch.BatchObject.Properties.newBuilder(); - var nonRef = com.google.protobuf.Struct.newBuilder(); var singleRef = new ArrayList(); var multiRef = new ArrayList(); - collection - .propertiesReader(insert.properties()).readProperties() - .entrySet().stream().forEach(entry -> { - try { - var protoValue = marshalValue(entry.getValue()); - if (protoValue == null) { - return; - } - nonRef.putFields(entry.getKey(), protoValue); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("marshal property " + entry.getKey(), e); - } - }); - insert.references() .entrySet().stream().forEach(entry -> { var references = entry.getValue(); @@ -173,16 +157,16 @@ public static void buildObject(WeaviateProtoBatch.BatchObject.Builder object } }); - properties + var nonRef = marshalStruct(collection.propertiesReader(insert.properties()).readProperties()); + object.setProperties(WeaviateProtoBatch.BatchObject.Properties.newBuilder() .setNonRefProperties(nonRef) .addAllSingleTargetRefProps(singleRef) - .addAllMultiTargetRefProps(multiRef); - - object.setProperties(properties); + .addAllMultiTargetRefProps(multiRef)); Debug.printProto(object); } + @SuppressWarnings("unchecked") private static com.google.protobuf.Value marshalValue(Object value) { var protoValue = com.google.protobuf.Value.newBuilder(); @@ -282,35 +266,30 @@ private static com.google.protobuf.Value marshalValue(Object value) { protoValue.setListValue(com.google.protobuf.ListValue.newBuilder() .addAllValues(values) .build()); - } else if (value instanceof Map v) { - var protoNested = com.google.protobuf.Struct.newBuilder(); - v.entrySet().stream() - .forEach(nested -> { - var nestedValue = marshalValue(nested.getValue()); - if (nestedValue == null) { - return; - } - protoNested.putFields((String) nested.getKey(), nestedValue); - }); - protoValue.setStructValue(protoNested); + } else if (value instanceof Map properties) { + protoValue.setStructValue(marshalStruct((Map) properties)); } else if (value instanceof Record r) { - @SuppressWarnings("unchecked") CollectionDescriptor recordDescriptor = (CollectionDescriptor) CollectionDescriptor .ofClass(r.getClass()); - var protoRecord = com.google.protobuf.Struct.newBuilder(); - recordDescriptor.propertiesReader(r).readProperties().entrySet().stream() - .forEach(recordField -> { - var nestedValue = marshalValue(recordField.getValue()); - if (nestedValue == null) { - return; - } - protoRecord.putFields((String) recordField.getKey(), nestedValue); - }); - protoValue.setStructValue(protoRecord); + var properties = recordDescriptor.propertiesReader(r).readProperties(); + protoValue.setStructValue(marshalStruct(properties)); } else { throw new IllegalArgumentException("data type " + value.getClass() + " is not supported"); } return protoValue.build(); } + + private static com.google.protobuf.Struct marshalStruct(Map properties) { + var struct = com.google.protobuf.Struct.newBuilder(); + properties.entrySet().stream() + .forEach(entry -> { + var nestedValue = marshalValue(entry.getValue()); + if (nestedValue == null) { + return; + } + struct.putFields((String) entry.getKey(), nestedValue); + }); + return struct.build(); + } } From cafb7ae88c87819d1ebd1436c08283473f30e8fc Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 7 Oct 2025 17:22:58 +0200 Subject: [PATCH 5/9] feat: support object[] properties --- .../integration/CollectionsITest.java | 21 ++++++++-- .../io/weaviate/integration/DataITest.java | 38 +++++++++++++------ .../io/weaviate/integration/ORMITest.java | 4 ++ .../client6/v1/api/collections/DataType.java | 2 +- .../collections/data/InsertManyRequest.java | 27 +++++++++++++ .../api/collections/query/QueryRequest.java | 9 +++++ .../client6/v1/internal/orm/MapBuilder.java | 5 +++ .../client6/v1/internal/orm/PojoBuilder.java | 7 +++- .../v1/internal/orm/PropertiesBuilder.java | 2 + 9 files changed, 98 insertions(+), 17 deletions(-) diff --git a/src/it/java/io/weaviate/integration/CollectionsITest.java b/src/it/java/io/weaviate/integration/CollectionsITest.java index f37c1eb72..e70503723 100644 --- a/src/it/java/io/weaviate/integration/CollectionsITest.java +++ b/src/it/java/io/weaviate/integration/CollectionsITest.java @@ -201,17 +201,30 @@ public void testNestedProperties() throws IOException, Exception { nsBuildings, c -> c.properties( Property.object("address", p -> p.nestedProperties( Property.text("street"), - Property.integer("buildingNr"), - Property.bool("isOneWay"))))); + Property.integer("building_nr"), + Property.bool("isOneWay"))), + Property.objectArray("apartments", p -> p.nestedProperties( + Property.integer("door_nr"), + Property.number("area"))))); var config = client.collections.getConfig(nsBuildings); - Assertions.assertThat(config).get() + var properties = Assertions.assertThat(config).get() .extracting(CollectionConfig::properties, InstanceOfAssertFactories.list(Property.class)) - .hasSize(1).first() + .hasSize(2).actual(); + + Assertions.assertThat(properties.get(0)) .returns("address", Property::propertyName) + .returns(DataType.OBJECT, p -> p.dataTypes().get(0)) .extracting(Property::nestedProperties, InstanceOfAssertFactories.list(Property.class)) .extracting(Property::dataTypes).extracting(types -> types.get(0)) .containsExactly(DataType.TEXT, DataType.INT, DataType.BOOL); + + Assertions.assertThat(properties.get(1)) + .returns("apartments", Property::propertyName) + .returns(DataType.OBJECT_ARRAY, p -> p.dataTypes().get(0)) + .extracting(Property::nestedProperties, InstanceOfAssertFactories.list(Property.class)) + .extracting(Property::dataTypes).extracting(types -> types.get(0)) + .containsExactly(DataType.INT, DataType.NUMBER); } } diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index abb627c47..38812b877 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -433,6 +433,9 @@ public void testDataTypes() throws IOException { Property.uuidArray("prop_uuid_array"), Property.textArray("prop_text_array"), Property.object("prop_object", + p -> p.nestedProperties( + Property.text("marco"))), + Property.objectArray("prop_object_array", p -> p.nestedProperties( Property.text("marco"))))); @@ -454,7 +457,8 @@ public void testDataTypes() throws IOException { Map.entry("prop_date_array", List.of(now, now)), Map.entry("prop_uuid_array", List.of(uuid, uuid)), Map.entry("prop_text_array", List.of("a", "b", "c")), - Map.entry("prop_object", Map.of("marco", "polo"))); + Map.entry("prop_object", Map.of("marco", "polo")), + Map.entry("prop_object_array", List.of(Map.of("marco", "polo")))); // Act var object = types.data.insert(want); @@ -483,19 +487,31 @@ public void testNestedProperties_insertMany() throws IOException { nsBuildings, c -> c.properties( Property.object("address", p -> p.nestedProperties( Property.text("street"), - Property.integer("buildingNr"), - Property.bool("isOneWay"))))); + Property.integer("building_nr"), + Property.bool("isOneWay"))), + Property.objectArray("apartments", p -> p.nestedProperties( + Property.integer("door_nr"), + Property.number("area"))))); var buildings = client.collections.use(nsBuildings); - Map house_1 = Map.of("address", Map.of( - "street", "Burggasse", - "building_nr", 51, - "isOneWay", true)); - Map house_2 = Map.of("address", new Address( - "Port Mariland St.", - 111, - false)); + Map house_1 = Map.of( + "address", Map.of( + "street", "Burggasse", + "building_nr", 51, + "isOneWay", true), + "apartments", List.of( + Map.of("door_nr", 11, "area", 42.2), + Map.of("door_nr", 12, "area", 26.7))); + Map house_2 = Map.of( + "address", new Address( + "Port Mariland St.", + 111, + false), + "apartments", new Map[] { + Map.of("door_nr", 21, "area", 42.2), + Map.of("door_nr", 22, "area", 26.7), + }); // Act var result = buildings.data.insertMany(house_1, house_2); diff --git a/src/it/java/io/weaviate/integration/ORMITest.java b/src/it/java/io/weaviate/integration/ORMITest.java index 16b5b3717..e732738fb 100644 --- a/src/it/java/io/weaviate/integration/ORMITest.java +++ b/src/it/java/io/weaviate/integration/ORMITest.java @@ -350,4 +350,8 @@ public void test_partialScan() throws IOException { .returns(true, Song::hasAward) .returns(null, Song::monthlyListeners); } + + @Test + public void test_nestedProperties() throws IOException { + } } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/DataType.java b/src/main/java/io/weaviate/client6/v1/api/collections/DataType.java index 7b99e8470..67044e22c 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/DataType.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/DataType.java @@ -34,5 +34,5 @@ public interface DataType { */ public static final Set KNOWN_TYPES = ImmutableSet.of( TEXT, INT, BLOB, BOOL, DATE, UUID, NUMBER, OBJECT, - TEXT_ARRAY, INT_ARRAY, NUMBER_ARRAY, BOOL_ARRAY, DATE_ARRAY, UUID_ARRAY); + TEXT_ARRAY, INT_ARRAY, NUMBER_ARRAY, BOOL_ARRAY, DATE_ARRAY, UUID_ARRAY, OBJECT_ARRAY); } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java b/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java index fff3ba479..4475f178d 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java @@ -200,6 +200,15 @@ private static com.google.protobuf.Value marshalValue(Object value) { protoListValue.setBoolValue(lv); } else if (listValue instanceof Number lv) { protoListValue.setNumberValue(lv.doubleValue()); + } else if (listValue instanceof Map properties) { + protoListValue.setStructValue(marshalStruct((Map) properties)); + } else if (listValue instanceof Record r) { + CollectionDescriptor recordDescriptor = (CollectionDescriptor) CollectionDescriptor + .ofClass(r.getClass()); + var properties = recordDescriptor.propertiesReader(r).readProperties(); + protoListValue.setStructValue(marshalStruct(properties)); + } else { + throw new IllegalArgumentException("data type " + value.getClass() + " is not supported"); } return protoListValue.build(); }) @@ -259,6 +268,24 @@ private static com.google.protobuf.Value marshalValue(Object value) { } else if (value instanceof Double[] v) { values = Arrays.stream(v) .map(lv -> com.google.protobuf.Value.newBuilder().setNumberValue(lv).build()).toList(); + } else if (value instanceof Map[] v) { + values = Arrays.stream(v) + .map(lv -> com.google.protobuf.Value.newBuilder() + .setStructValue(marshalStruct((Map) lv)) + .build()) + .toList(); + } else if (value instanceof Record[] v) { + values = Arrays.stream(v) + .map(lv -> { + // Get the descriptor for each iteration in case the array is heterogenous. + final CollectionDescriptor recordDescriptor = (CollectionDescriptor) CollectionDescriptor + .ofClass(lv.getClass()); + var properties = recordDescriptor.propertiesReader(lv).readProperties(); + return com.google.protobuf.Value.newBuilder() + .setStructValue(marshalStruct(properties)) + .build(); + }) + .toList(); } else { throw new IllegalArgumentException("array type " + value.getClass() + " is not supported"); } 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 fd5908b76..11d31b4e5 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 @@ -254,6 +254,15 @@ private static void setProperty(String property, WeaviateProtoProperties.Val var dates = list.getDateValues().getValuesList().stream() .map(DateUtil::fromISO8601).toList(); builder.setOffsetDateTimeArray(property, dates); + } else if (list.hasObjectValues()) { + List objects = list.getObjectValues().getValuesList().stream() + .map(object -> { + var properties = descriptor.propertiesBuilder(); + object.getFieldsMap().entrySet().stream() + .forEach(entry -> setProperty(entry.getKey(), entry.getValue(), properties, descriptor)); + return properties.build(); + }).toList(); + builder.setNestedObjectArray(property, objects); } } else if (value.hasObjectValue()) { var object = value.getObjectValue(); 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 16d8219bd..0c044e3c9 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 @@ -84,6 +84,11 @@ public void setNestedObject(String property, Object value) { properties.put(property, value); } + @Override + public void setNestedObjectArray(String property, List value) { + properties.put(property, value); + } + @Override public Map build() { return properties; 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 da9a56309..085811565 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 @@ -229,7 +229,12 @@ public void setOffsetDateTimeArray(String propertyName, List val @Override public void setNestedObject(String propertyName, Object value) { - setValue(propertyName, value); + throw new UnsupportedOperationException("Unimplemented method 'setNestedObjectArray'"); + } + + @Override + public void setNestedObjectArray(String property, List value) { + throw new UnsupportedOperationException("Unimplemented method 'setNestedObjectArray'"); } @Override 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 97445d431..81e8ec5e4 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 @@ -35,5 +35,7 @@ public interface PropertiesBuilder { void setNestedObject(String property, Object value); + void setNestedObjectArray(String property, List value); + T build(); } From d0a83a5dca2368a1d2dbe32c89125f06495165ea Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 7 Oct 2025 17:25:08 +0200 Subject: [PATCH 6/9] test: use unique collection names --- src/it/java/io/weaviate/integration/CollectionsITest.java | 2 +- src/it/java/io/weaviate/integration/DataITest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/it/java/io/weaviate/integration/CollectionsITest.java b/src/it/java/io/weaviate/integration/CollectionsITest.java index e70503723..975ebba76 100644 --- a/src/it/java/io/weaviate/integration/CollectionsITest.java +++ b/src/it/java/io/weaviate/integration/CollectionsITest.java @@ -195,7 +195,7 @@ public void testInvalidCollectionName() throws IOException { @Test public void testNestedProperties() throws IOException, Exception { - var nsBuildings = "Buildings"; + var nsBuildings = ns("Buildings"); client.collections.create( nsBuildings, c -> c.properties( diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index 38812b877..693eccea4 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -481,7 +481,7 @@ record Address( @Test public void testNestedProperties_insertMany() throws IOException { // Arrange - var nsBuildings = "Buildings"; + var nsBuildings = ns("Buildings"); client.collections.create( nsBuildings, c -> c.properties( From 618b22d84535882bbd2ba7b1a4e086a41af97641 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 7 Oct 2025 17:29:23 +0200 Subject: [PATCH 7/9] test: bork local connection to prompt exception --- .../io/weaviate/client6/v1/api/WeaviateClientAsyncTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/weaviate/client6/v1/api/WeaviateClientAsyncTest.java b/src/test/java/io/weaviate/client6/v1/api/WeaviateClientAsyncTest.java index e538de7a0..60e46de46 100644 --- a/src/test/java/io/weaviate/client6/v1/api/WeaviateClientAsyncTest.java +++ b/src/test/java/io/weaviate/client6/v1/api/WeaviateClientAsyncTest.java @@ -14,7 +14,7 @@ public void testFailedConnection() { @Test(expected = WeaviateConnectException.class) public void testFailedConnection_Local() { - WeaviateClientAsync.connectToLocal(); + WeaviateClientAsync.connectToLocal(conn -> conn.port(1234)); } @Test(expected = WeaviateConnectException.class) From a0c9ca2cad32d0a7df52a3614e7a51e870eccb14 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 7 Oct 2025 17:30:12 +0200 Subject: [PATCH 8/9] chore: force GSON to respect int/long Default behaviour is converting everything to floats, which may be confusing especially when logging request bodies --- src/main/java/io/weaviate/client6/v1/internal/json/JSON.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/weaviate/client6/v1/internal/json/JSON.java b/src/main/java/io/weaviate/client6/v1/internal/json/JSON.java index 3d2e00089..e1f0040f2 100644 --- a/src/main/java/io/weaviate/client6/v1/internal/json/JSON.java +++ b/src/main/java/io/weaviate/client6/v1/internal/json/JSON.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.ToNumberPolicy; import com.google.gson.reflect.TypeToken; import io.weaviate.client6.v1.internal.orm.PropertyFieldNamingStrategy; @@ -49,6 +50,8 @@ public final class JSON { // ORM FieldNaminsStrategy ------------------------------------------------ gsonBuilder.setFieldNamingStrategy(PropertyFieldNamingStrategy.INSTANCE); + + gsonBuilder.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE); gson = gsonBuilder.create(); } From e4e12f029731bb8187fa56c5036855af682efd97 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Thu, 9 Oct 2025 11:10:54 +0200 Subject: [PATCH 9/9] docs: mention nested properties in the README --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 1ba9b291d..5a77b0020 100644 --- a/README.md +++ b/README.md @@ -668,10 +668,22 @@ for (var object : result.objects()) { } ``` +When _ingetsting_ data, Java records can be used to represent nested object properties: + +```java +record MusicVideo(@Property("link") String url, long runtimeSeconds) {} + +songs.data.insert(Map.of( + "title", "Billie Jean", + "musicVideo", new MusicVideo("https://youtube.com/billijean", 294L), +)); +``` + We want to stress that this ORM's focus is on improving type-safety around object properties and simplifying serialization/deserialization. It is intentionally kept minimal and as such has the following limitations: - **Does not support BLOB properties.** On the wire, blob properties are represented as base64-encoded strings, and both logically map to the Java's `String`. Presently there isn't a good way for the client to deduce which property type should be created, so it always maps `Sting -> TEXT`. - **Limited configuration options.** Vector indices, replication, multi-tenancy, and such need to be configured via a tucked builder in `.create(..., here -> here)`. +- **Cannot include nested objects.** Java records can be used as nested properties in a `Map`, but cannot include nested properties themselves. - **Does not support cross-references.** Properties and Cross-References are conceptually and "physically" separated in Weaviate' client libraries, so doing something like in the snippet below is not supported. ```java