From 1e37d7f18fed5239112f38aa5e7a171a03f0ba49 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Fri, 24 Oct 2025 12:33:35 +0200 Subject: [PATCH 1/2] feat: define PhoneNumber and GeoCoordinates data type --- .../io/weaviate/integration/DataITest.java | 4 ++ .../client6/v1/api/collections/DataType.java | 5 +- .../v1/api/collections/GeoCoordinates.java | 8 +++ .../v1/api/collections/PhoneNumber.java | 59 +++++++++++++++++++ .../client6/v1/api/collections/Property.java | 40 ++++++++++++- .../v1/api/collections/query/Where.java | 8 +-- 6 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 src/main/java/io/weaviate/client6/v1/api/collections/GeoCoordinates.java create mode 100644 src/main/java/io/weaviate/client6/v1/api/collections/PhoneNumber.java diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index ac4a004b5..a8f03719d 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -14,6 +14,7 @@ import io.weaviate.ConcurrentTest; import io.weaviate.client6.v1.api.WeaviateApiException; import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.PhoneNumber; import io.weaviate.client6.v1.api.collections.Property; import io.weaviate.client6.v1.api.collections.ReferenceProperty; import io.weaviate.client6.v1.api.collections.VectorConfig; @@ -432,6 +433,8 @@ public void testDataTypes() throws IOException { Property.dateArray("prop_date_array"), Property.uuidArray("prop_uuid_array"), Property.textArray("prop_text_array"), + Property.phoneNumber("prop_phone_number"), + Property.geoCoordinates("prop_geo_coordinates"), Property.object("prop_object", p -> p.nestedProperties( Property.text("marco"))), @@ -457,6 +460,7 @@ 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_phone_number", PhoneNumber.international("+380 95 1433336")), Map.entry("prop_object", Map.of("marco", "polo")), Map.entry("prop_object_array", List.of(Map.of("marco", "polo")))); 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 67044e22c..62f529be8 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 @@ -20,6 +20,8 @@ public interface DataType { public static final String UUID_ARRAY = "uuid[]"; public static final String OBJECT = "object"; public static final String OBJECT_ARRAY = "object[]"; + public static final String PHONE_NUMBER = "phoneNumber"; + public static final String GEO_COORDINATES = "geoCoordinates"; /** * Scalar/array types which Weaviate and WeaviateClient recognize. @@ -34,5 +36,6 @@ 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, OBJECT_ARRAY); + TEXT_ARRAY, INT_ARRAY, NUMBER_ARRAY, BOOL_ARRAY, DATE_ARRAY, UUID_ARRAY, OBJECT_ARRAY, + PHONE_NUMBER, GEO_COORDINATES); } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/GeoCoordinates.java b/src/main/java/io/weaviate/client6/v1/api/collections/GeoCoordinates.java new file mode 100644 index 000000000..80e10bedc --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/GeoCoordinates.java @@ -0,0 +1,8 @@ +package io.weaviate.client6.v1.api.collections; + +import com.google.gson.annotations.SerializedName; + +public record GeoCoordinates( + @SerializedName("latitude") float latitude, + @SerializedName("longitude") float longitude) { +} diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/PhoneNumber.java b/src/main/java/io/weaviate/client6/v1/api/collections/PhoneNumber.java new file mode 100644 index 000000000..3e9f5eb32 --- /dev/null +++ b/src/main/java/io/weaviate/client6/v1/api/collections/PhoneNumber.java @@ -0,0 +1,59 @@ +package io.weaviate.client6.v1.api.collections; + +import com.google.gson.annotations.SerializedName; + +public record PhoneNumber( + /** Raw input data provided at creation. */ + @SerializedName("input") String rawInput, + /** + * ISO 3166-1 alpha-2 country code. Required only if the raw input does not + * include an explicit country code, e.g. {@code +31}. Only present if provided + * by user. + */ + @SerializedName("defaultCountry") String defaultCountry, + /** Numerical country code. Returned by Weaviate on read. */ + @SerializedName("countryCode") Integer countryCode, + /** + * Phone number with numerical country code prepended. + * Returned by Weaviate on read. + */ + @SerializedName("internationalFormatted") String internationalFormatted, + /** + * Numerical representation of the national number. + * Returned by Weaviate on read. + */ + @SerializedName("national") Integer national, + /** + * Formatted national number. + * Returned by Weaviate on read. + */ + @SerializedName("nationalFormatted") String nationalFormatted, + /** + * Whether the server recognized this number as valid. + * Returned by Weaviate on read. + */ + @SerializedName("valid") Boolean valid) { + + /** + * Create national phone number (without explicit country code), + * e.g. {@code "020 1234567"} + * + * @param country ISO 3166-1 alpha-2 country code. + * @param phoneNumber Phone number. + * @return PhoneNumber + */ + public static PhoneNumber national(String country, String phoneNumber) { + return new PhoneNumber(phoneNumber, country, null, null, null, null, null); + } + + /** + * Create a phone number with explicit country code, + * e.g. {@code "+31 20 1234567"} + * + * @param phoneNumber Phone number. + * @return PhoneNumber + */ + public static PhoneNumber international(String phoneNumber) { + return new PhoneNumber(phoneNumber, null, null, null, null, null, null); + } +} 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 f3e69cf3a..039e819f2 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 @@ -299,7 +299,7 @@ public static Property objectArray(String name) { } /** - * Create a {@code objectArray[]} property with additional configuration. + * Create a {@code object[]} property with additional configuration. * * @param name Property name. * @param fn Lambda expression for optional parameters. @@ -308,6 +308,44 @@ public static Property objectArray(String name, Function> fn) { + return newProperty(name, DataType.PHONE_NUMBER, fn); + } + + /** + * Create a {@code geoCoordinates} property. + * + * @param name Property name. + */ + public static Property geoCoordinates(String name) { + return geoCoordinates(name, ObjectBuilder.identity()); + } + + /** + * Create a {@code geoCoordinates} property with additional configuration. + * + * @param name Property name. + * @param fn Lambda expression for optional parameters. + */ + public static Property geoCoordinates(String name, Function> fn) { + return newProperty(name, DataType.GEO_COORDINATES, fn); + } + private static Property newProperty(String name, String dataType, Function> fn) { return fn.apply(new Builder(name, dataType)).build(); } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/Where.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/Where.java index 55dee289f..56b49b927 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/Where.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/Where.java @@ -826,11 +826,11 @@ public String toString() { } private static class GeoRangeOperand implements WhereOperand { - private final Float lat; - private final Float lon; - private final Float distance; + private final float lat; + private final float lon; + private final float distance; - private GeoRangeOperand(Float lat, Float lon, Float distance) { + private GeoRangeOperand(float lat, float lon, float distance) { this.lat = lat; this.lon = lon; this.distance = distance; From 94a9e9932f941bf5469cb1cdbfdc3e3ff3eb0bdc Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Fri, 24 Oct 2025 13:22:12 +0200 Subject: [PATCH 2/2] feat: read/write PhoneNumber and GeoCoordinates data --- .../io/weaviate/integration/DataITest.java | 8 +++-- .../io/weaviate/integration/ORMITest.java | 29 ++++++++++++++----- .../collections/data/InsertManyRequest.java | 19 ++++++++++++ .../api/collections/query/QueryResponse.java | 17 +++++++++++ .../client6/v1/internal/orm/MapBuilder.java | 15 +++++++++- .../client6/v1/internal/orm/PojoBuilder.java | 13 +++++++++ .../v1/internal/orm/PojoDescriptor.java | 5 ++++ .../v1/internal/orm/PropertiesBuilder.java | 7 +++++ 8 files changed, 103 insertions(+), 10 deletions(-) diff --git a/src/it/java/io/weaviate/integration/DataITest.java b/src/it/java/io/weaviate/integration/DataITest.java index a8f03719d..4b29ee70e 100644 --- a/src/it/java/io/weaviate/integration/DataITest.java +++ b/src/it/java/io/weaviate/integration/DataITest.java @@ -14,6 +14,7 @@ import io.weaviate.ConcurrentTest; import io.weaviate.client6.v1.api.WeaviateApiException; import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.GeoCoordinates; import io.weaviate.client6.v1.api.collections.PhoneNumber; import io.weaviate.client6.v1.api.collections.Property; import io.weaviate.client6.v1.api.collections.ReferenceProperty; @@ -461,6 +462,7 @@ public void testDataTypes() throws IOException { Map.entry("prop_uuid_array", List.of(uuid, uuid)), Map.entry("prop_text_array", List.of("a", "b", "c")), Map.entry("prop_phone_number", PhoneNumber.international("+380 95 1433336")), + Map.entry("prop_geo_coordinates", new GeoCoordinates(1f, 2f)), Map.entry("prop_object", Map.of("marco", "polo")), Map.entry("prop_object_array", List.of(Map.of("marco", "polo")))); @@ -472,8 +474,10 @@ public void testDataTypes() throws IOException { Assertions.assertThat(got).get() .extracting(WeaviateObject::properties) .asInstanceOf(InstanceOfAssertFactories.map(String.class, Object.class)) - .containsAllEntriesOf(want); - + // Most of PhoneNumber fields are only present on read and are null on write. + .usingRecursiveComparison() + .withComparatorForType(ORMITest::comparePhoneNumbers, PhoneNumber.class) + .isEqualTo(want); } record Address( diff --git a/src/it/java/io/weaviate/integration/ORMITest.java b/src/it/java/io/weaviate/integration/ORMITest.java index 1d4a25c5e..3d066b5e5 100644 --- a/src/it/java/io/weaviate/integration/ORMITest.java +++ b/src/it/java/io/weaviate/integration/ORMITest.java @@ -15,6 +15,8 @@ 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.GeoCoordinates; +import io.weaviate.client6.v1.api.collections.PhoneNumber; 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; @@ -79,7 +81,10 @@ static record Thing( Boolean booleanBoxed, boolean[] booleanArray, Boolean[] booleanBoxedArray, - List booleanBoxedList) { + List booleanBoxedList, + + PhoneNumber phoneNumber, + GeoCoordinates geoCoordinates) { } @BeforeClass @@ -150,14 +155,18 @@ public void test_createCollection() throws Exception { Map.entry("booleanBoxed", "boolean"), Map.entry("booleanArray", "boolean[]"), Map.entry("booleanBoxedArray", "boolean[]"), - Map.entry("booleanBoxedList", "boolean[]")); + Map.entry("booleanBoxedList", "boolean[]"), + + Map.entry("phoneNumber", "phoneNumber"), + Map.entry("geoCoordinates", "geoCoordinates")); } - private final RecursiveComparisonConfiguration COMPARISON_CONFIG = RecursiveComparisonConfiguration.builder() + private static final RecursiveComparisonConfiguration COMPARISON_CONFIG = RecursiveComparisonConfiguration.builder() // Assertj is having a really bad time comparing List, // so we'll just always return true here. .withComparatorForFields((a, b) -> 0, "floatBoxedList") .withComparatorForType((a, b) -> Double.compare(a.doubleValue(), b.doubleValue()), Number.class) + .withComparatorForType(ORMITest::comparePhoneNumbers, PhoneNumber.class) .build(); @Test @@ -219,7 +228,10 @@ public void test_insertAndQuery() throws Exception { boolean_, new boolean[] { boolean_ }, new Boolean[] { boolean_ }, - List.of(boolean_)); + List.of(boolean_), + + PhoneNumber.international("+380 95 1433336"), + new GeoCoordinates(1f, 2f)); var things = client.collections.use(Thing.class); @@ -294,7 +306,10 @@ public void test_insertManyAndQuery() throws Exception { boolean_, new boolean[] { boolean_ }, new Boolean[] { boolean_ }, - List.of(boolean_)); + List.of(boolean_), + + PhoneNumber.international("+380 95 1433336"), + new GeoCoordinates(1f, 2f)); var things = client.collections.use(Thing.class); @@ -351,7 +366,7 @@ public void test_partialScan() throws IOException { .returns(null, Song::monthlyListeners); } - @Test - public void test_nestedProperties() throws IOException { + static int comparePhoneNumbers(PhoneNumber phone1, PhoneNumber phone2) { + return phone1.rawInput().compareTo(phone2.rawInput()); } } 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 7513665ed..65da1dced 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 @@ -8,7 +8,9 @@ import java.util.UUID; import io.weaviate.client6.v1.api.collections.CollectionHandleDefaults; +import io.weaviate.client6.v1.api.collections.GeoCoordinates; import io.weaviate.client6.v1.api.collections.ObjectMetadata; +import io.weaviate.client6.v1.api.collections.PhoneNumber; import io.weaviate.client6.v1.api.collections.WeaviateObject; import io.weaviate.client6.v1.internal.MapUtil; import io.weaviate.client6.v1.internal.grpc.ByteStringUtil; @@ -181,6 +183,23 @@ private static com.google.protobuf.Value marshalValue(Object value) { protoValue.setBoolValue(v.booleanValue()); } else if (value instanceof Number v) { protoValue.setNumberValue(v.doubleValue()); + } else if (value instanceof PhoneNumber phone) { + var phoneProto = com.google.protobuf.Struct.newBuilder(); + if (phone.rawInput() != null) { + var input = com.google.protobuf.Value.newBuilder().setStringValue(phone.rawInput()); + phoneProto.putFields("input", input.build()); + } + if (phone.defaultCountry() != null) { + var defaultCountry = com.google.protobuf.Value.newBuilder().setStringValue(phone.defaultCountry()); + phoneProto.putFields("defaultCountry", defaultCountry.build()); + } + protoValue.setStructValue(phoneProto); + } else if (value instanceof GeoCoordinates geo) { + var latitude = com.google.protobuf.Value.newBuilder().setNumberValue(geo.latitude()); + var longitude = com.google.protobuf.Value.newBuilder().setNumberValue(geo.longitude()); + protoValue.setStructValue(com.google.protobuf.Struct.newBuilder() + .putFields("latitude", latitude.build()) + .putFields("longitude", longitude.build())); } else if (value instanceof List v) { protoValue.setListValue( com.google.protobuf.ListValue.newBuilder() diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/QueryResponse.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/QueryResponse.java index 1160b0255..98889c492 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/QueryResponse.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/QueryResponse.java @@ -6,7 +6,9 @@ import java.util.UUID; import java.util.stream.Stream; +import io.weaviate.client6.v1.api.collections.GeoCoordinates; import io.weaviate.client6.v1.api.collections.ObjectMetadata; +import io.weaviate.client6.v1.api.collections.PhoneNumber; import io.weaviate.client6.v1.api.collections.Vectors; import io.weaviate.client6.v1.api.collections.WeaviateObject; import io.weaviate.client6.v1.internal.DateUtil; @@ -159,6 +161,21 @@ static void setProperty(String property, WeaviateProtoProperties.V builder.setOffsetDateTime(property, DateUtil.fromISO8601(value.getDateValue())); } else if (value.hasUuidValue()) { builder.setUuid(property, UUID.fromString(value.getUuidValue())); + } else if (value.hasPhoneValue()) { + var phone = value.getPhoneValue(); + builder.setPhoneNumber(property, new PhoneNumber( + phone.getInput(), + phone.getDefaultCountry(), + Long.valueOf(phone.getCountryCode()).intValue(), + phone.getInternationalFormatted(), + Long.valueOf(phone.getNational()).intValue(), + phone.getNationalFormatted(), + phone.getValid())); + } else if (value.hasGeoValue()) { + var geo = value.getGeoValue(); + builder.setGeoCoordinates(property, new GeoCoordinates( + geo.getLatitude(), + geo.getLongitude())); } else if (value.hasListValue()) { var list = value.getListValue(); if (list.hasTextValues()) { 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 0c044e3c9..7e76fa9f8 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 @@ -6,6 +6,9 @@ import java.util.Map; import java.util.UUID; +import io.weaviate.client6.v1.api.collections.GeoCoordinates; +import io.weaviate.client6.v1.api.collections.PhoneNumber; + public class MapBuilder implements PropertiesBuilder> { private final Map properties = new HashMap<>(); @@ -89,8 +92,18 @@ public void setNestedObjectArray(String property, List value) properties.put(property, value); } + @Override + public void setPhoneNumber(String property, PhoneNumber value) { + properties.put(property, value); + } + + @Override + public void setGeoCoordinates(String property, GeoCoordinates value) { + properties.put(property, value); + } + @Override public Map build() { - return properties; + return new HashMap<>(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 085811565..e5d08972b 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 @@ -10,6 +10,9 @@ import org.apache.commons.lang3.ArrayUtils; +import io.weaviate.client6.v1.api.collections.GeoCoordinates; +import io.weaviate.client6.v1.api.collections.PhoneNumber; + final class PojoBuilder implements PropertiesBuilder { private static final Map, Object> PRIMITIVE_DEFAULTS; @@ -237,6 +240,16 @@ public void setNestedObjectArray(String property, List value) throw new UnsupportedOperationException("Unimplemented method 'setNestedObjectArray'"); } + @Override + public void setPhoneNumber(String propertyName, PhoneNumber value) { + setValue(propertyName, value); + } + + @Override + public void setGeoCoordinates(String propertyName, GeoCoordinates 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 0b5724153..4dbf64a89 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 @@ -16,6 +16,8 @@ import com.google.gson.reflect.TypeToken; import io.weaviate.client6.v1.api.collections.CollectionConfig; +import io.weaviate.client6.v1.api.collections.GeoCoordinates; +import io.weaviate.client6.v1.api.collections.PhoneNumber; import io.weaviate.client6.v1.api.collections.Property; import io.weaviate.client6.v1.api.collections.annotations.Collection; import io.weaviate.client6.v1.internal.ObjectBuilder; @@ -65,6 +67,9 @@ final class PojoDescriptor implements CollectionDescriptor put(Double[].class, Property::numberArray); put(Map.class, Property::object); + + put(PhoneNumber.class, Property::phoneNumber); + put(GeoCoordinates.class, Property::geoCoordinates); } }; 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 81e8ec5e4..7f5236485 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 @@ -4,6 +4,9 @@ import java.util.List; import java.util.UUID; +import io.weaviate.client6.v1.api.collections.GeoCoordinates; +import io.weaviate.client6.v1.api.collections.PhoneNumber; + public interface PropertiesBuilder { void setNull(String property); @@ -37,5 +40,9 @@ public interface PropertiesBuilder { void setNestedObjectArray(String property, List value); + void setPhoneNumber(String property, PhoneNumber value); + + void setGeoCoordinates(String property, GeoCoordinates value); + T build(); }