From 28ff4753460482d02f8fc5b93047d8435c90e60e Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Wed, 19 Nov 2025 18:42:03 +0100 Subject: [PATCH 1/5] feat: add isNull / isNotNull filter --- .../io/weaviate/integration/SearchITest.java | 33 +++++++++++++++++-- .../v1/api/collections/query/Filter.java | 15 +++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/it/java/io/weaviate/integration/SearchITest.java b/src/it/java/io/weaviate/integration/SearchITest.java index 32c86c493..7bca498c0 100644 --- a/src/it/java/io/weaviate/integration/SearchITest.java +++ b/src/it/java/io/weaviate/integration/SearchITest.java @@ -30,13 +30,13 @@ import io.weaviate.client6.v1.api.collections.generate.GenerativeObject; import io.weaviate.client6.v1.api.collections.generate.TaskOutput; import io.weaviate.client6.v1.api.collections.generative.DummyGenerative; +import io.weaviate.client6.v1.api.collections.query.Filter; import io.weaviate.client6.v1.api.collections.query.GroupBy; import io.weaviate.client6.v1.api.collections.query.Metadata; import io.weaviate.client6.v1.api.collections.query.QueryMetadata; import io.weaviate.client6.v1.api.collections.query.QueryResponseGroup; import io.weaviate.client6.v1.api.collections.query.SortBy; import io.weaviate.client6.v1.api.collections.query.Target; -import io.weaviate.client6.v1.api.collections.query.Filter; import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; import io.weaviate.client6.v1.api.collections.vectorindex.MultiVector; import io.weaviate.containers.Container; @@ -608,9 +608,9 @@ public void testGenerative_bm25() throws IOException { .hasSize(2) .allSatisfy(obj -> { Assertions.assertThat(obj).as("uuid shorthand") - .returns(obj.uuid(), GenerativeObject::uuid); + .returns(obj.uuid(), GenerativeObject::uuid); Assertions.assertThat(obj).as("vectors shorthand") - .returns(obj.vectors(), GenerativeObject::vectors); + .returns(obj.vectors(), GenerativeObject::vectors); }) .extracting(GenerativeObject::generative) .allSatisfy(generated -> { @@ -673,4 +673,31 @@ public void testGenerative_bm25_groupBy() throws IOException { .extracting(TaskOutput::text, InstanceOfAssertFactories.STRING) .isNotBlank(); } + + @Test + public void test_filterIsNull() throws IOException { + // Arrange + var nsNulls = ns("Nulls"); + + var nulls = client.collections.create(nsNulls, + c -> c + .invertedIndex(idx -> idx.indexNulls(true)) + .properties(Property.text("never"))); + + var inserted = nulls.data.insertMany(Map.of(), Map.of("never", "notNull")); + Assertions.assertThat(inserted.errors()).isEmpty(); + + // Act + var isNull = nulls.query.fetchObjects(q -> q.filters(Filter.property("never").isNull())); + var isNotNull = nulls.query.fetchObjects(q -> q.filters(Filter.property("never").isNotNull())); + + // Assert + var isNull_1 = Assertions.assertThat(isNull.objects()) + .as("objects WHERE never IS NULL") + .hasSize(1).first().actual(); + var isNotNull_1 = Assertions.assertThat(isNotNull.objects()) + .as("objects WHERE never IS NOT NULL") + .hasSize(1).first().actual(); + Assertions.assertThat(isNull_1).isNotEqualTo(isNotNull_1); + } } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java index b7e9e6402..91a853dfc 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java @@ -22,6 +22,7 @@ private enum Operator { LESS_THAN_EQUAL("LessThenEqual", WeaviateProtoBase.Filters.Operator.OPERATOR_LESS_THAN_EQUAL), GREATER_THAN("GreaterThen", WeaviateProtoBase.Filters.Operator.OPERATOR_GREATER_THAN), GREATER_THAN_EQUAL("GreaterThenEqual", WeaviateProtoBase.Filters.Operator.OPERATOR_GREATER_THAN_EQUAL), + IS_NULL("IsNull", WeaviateProtoBase.Filters.Operator.OPERATOR_IS_NULL), LIKE("Like", WeaviateProtoBase.Filters.Operator.OPERATOR_LIKE), CONTAINS_ANY("ContainsAny", WeaviateProtoBase.Filters.Operator.OPERATOR_CONTAINS_ANY), CONTAINS_ALL("ContainsAll", WeaviateProtoBase.Filters.Operator.OPERATOR_CONTAINS_ALL), @@ -422,6 +423,20 @@ public Filter gte(Object value) { return new Filter(Operator.GREATER_THAN_EQUAL, left, fromObject(value)); } + // IsNull + // ------------------------------------------------------------------------ + public Filter isNull() { + return isNull(true); + } + + public Filter isNotNull() { + return isNull(false); + } + + public Filter isNull(boolean isNull) { + return new Filter(Operator.IS_NULL, left, new BooleanOperand(isNull)); + } + // Like // ------------------------------------------------------------------------ public Filter like(String value) { From 18cbb4a1f9d7dbbae85a35441119c5272e953ec9 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Wed, 19 Nov 2025 19:11:32 +0100 Subject: [PATCH 2/5] feat: add filters for create/update time --- .../io/weaviate/integration/SearchITest.java | 23 ++++++++ .../collections/query/BaseQueryOptions.java | 4 ++ .../collections/query/FetchObjectById.java | 2 - .../v1/api/collections/query/Filter.java | 56 +++++++++++++++++-- .../v1/api/collections/query/SortBy.java | 2 +- 5 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/it/java/io/weaviate/integration/SearchITest.java b/src/it/java/io/weaviate/integration/SearchITest.java index 7bca498c0..15a4de50e 100644 --- a/src/it/java/io/weaviate/integration/SearchITest.java +++ b/src/it/java/io/weaviate/integration/SearchITest.java @@ -1,6 +1,7 @@ package io.weaviate.integration; import java.io.IOException; +import java.time.OffsetDateTime; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -700,4 +701,26 @@ public void test_filterIsNull() throws IOException { .hasSize(1).first().actual(); Assertions.assertThat(isNull_1).isNotEqualTo(isNotNull_1); } + + @Test + public void test_filterCreateUpdateTime() throws IOException { + // Arrange + var now = OffsetDateTime.now().minusHours(1); + var nsCounter = ns("Counter"); + + var counter = client.collections.create(nsCounter, + c -> c + .invertedIndex(idx -> idx.indexTimestamps(true)) + .properties(Property.integer("count"))); + + counter.data.insert(Map.of("count", 0)); + + // Act + var beforeNow = counter.query.fetchObjects(q -> q.filters(Filter.createdAt().lt(now))); + var afterNow = counter.query.fetchObjects(q -> q.filters(Filter.createdAt().gt(now))); + + // Assert + Assertions.assertThat(beforeNow.objects()).isEmpty(); + Assertions.assertThat(afterNow.objects()).hasSize(1); + } } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/BaseQueryOptions.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/BaseQueryOptions.java index 64276e425..1786ce74d 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/BaseQueryOptions.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/BaseQueryOptions.java @@ -26,6 +26,10 @@ public record BaseQueryOptions( List returnMetadata, List includeVectors) { + static final String ID_PROPERTY = "_id"; + static final String CREATION_TIME_PROPERTY = "_creationTimeUnix"; + static final String LAST_UPDATE_TIME_PROPERTY = "_lastUpdateTimeUnix"; + private BaseQueryOptions(Builder, T> builder) { this( builder.limit, diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/FetchObjectById.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/FetchObjectById.java index 916edbb0d..3e3bdf512 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/FetchObjectById.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/FetchObjectById.java @@ -19,8 +19,6 @@ public record FetchObjectById( List returnMetadata, List includeVectors) implements QueryOperator { - static final String ID_PROPERTY = "_id"; - public static FetchObjectById of(String uuid) { return of(uuid, ObjectBuilder.identity()); } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java index 91a853dfc..a27129b7e 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java @@ -18,10 +18,10 @@ private enum Operator { // Comparison operators EQUAL("Equal", WeaviateProtoBase.Filters.Operator.OPERATOR_EQUAL), NOT_EQUAL("NotEqual", WeaviateProtoBase.Filters.Operator.OPERATOR_NOT_EQUAL), - LESS_THAN("LessThen", WeaviateProtoBase.Filters.Operator.OPERATOR_LESS_THAN), + LESS_THAN("LessThan", WeaviateProtoBase.Filters.Operator.OPERATOR_LESS_THAN), LESS_THAN_EQUAL("LessThenEqual", WeaviateProtoBase.Filters.Operator.OPERATOR_LESS_THAN_EQUAL), - GREATER_THAN("GreaterThen", WeaviateProtoBase.Filters.Operator.OPERATOR_GREATER_THAN), - GREATER_THAN_EQUAL("GreaterThenEqual", WeaviateProtoBase.Filters.Operator.OPERATOR_GREATER_THAN_EQUAL), + GREATER_THAN("GreaterThan", WeaviateProtoBase.Filters.Operator.OPERATOR_GREATER_THAN), + GREATER_THAN_EQUAL("GreaterThanEqual", WeaviateProtoBase.Filters.Operator.OPERATOR_GREATER_THAN_EQUAL), IS_NULL("IsNull", WeaviateProtoBase.Filters.Operator.OPERATOR_IS_NULL), LIKE("Like", WeaviateProtoBase.Filters.Operator.OPERATOR_LIKE), CONTAINS_ANY("ContainsAny", WeaviateProtoBase.Filters.Operator.OPERATOR_CONTAINS_ANY), @@ -111,7 +111,17 @@ public Filter not() { /** Filter by object UUID. */ public static FilterBuilder uuid() { - return property(FetchObjectById.ID_PROPERTY); + return property(BaseQueryOptions.ID_PROPERTY); + } + + /** Filter by object creation time. */ + public static DateProperty createdAt() { + return new DateProperty(BaseQueryOptions.CREATION_TIME_PROPERTY); + } + + /** Filter by object last update time. */ + public static DateProperty lastUpdatedAt() { + return new DateProperty(BaseQueryOptions.LAST_UPDATE_TIME_PROPERTY); } /** Filter by object property. */ @@ -635,6 +645,44 @@ public String toString() { } } + public static class DateProperty extends PathOperand { + private DateProperty(String propertyName) { + super(propertyName); + } + + public Filter eq(OffsetDateTime value) { + return new Filter(Operator.EQUAL, this, new DateOperand(value)); + } + + public Filter ne(OffsetDateTime value) { + return new Filter(Operator.NOT_EQUAL, this, new DateOperand(value)); + } + + public Filter gt(OffsetDateTime value) { + return new Filter(Operator.GREATER_THAN, this, new DateOperand(value)); + } + + public Filter gte(OffsetDateTime value) { + return new Filter(Operator.GREATER_THAN_EQUAL, this, new DateOperand(value)); + } + + public Filter lt(OffsetDateTime value) { + return new Filter(Operator.LESS_THAN, this, new DateOperand(value)); + } + + public Filter lte(OffsetDateTime value) { + return new Filter(Operator.LESS_THAN_EQUAL, this, new DateOperand(value)); + } + + public Filter containsAny(OffsetDateTime... values) { + return new Filter(Operator.CONTAINS_ANY, this, new DateArrayOperand(values)); + } + + public Filter containsNone(OffsetDateTime... values) { + return new Filter(Operator.CONTAINS_NONE, this, new DateArrayOperand(values)); + } + } + private static class TextOperand implements FilterOperand { private final String value; diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/SortBy.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/SortBy.java index 55ca1b3e4..ebab47aca 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/SortBy.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/SortBy.java @@ -26,7 +26,7 @@ public static SortBy property(String property) { * @see #desc() to sort in descending order. */ public static SortBy uuid() { - return property(FetchObjectById.ID_PROPERTY); + return property(BaseQueryOptions.ID_PROPERTY); } /** From e4fc6caca4ce5510bd8886fb62b5ede72fd3787b Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Thu, 20 Nov 2025 15:39:28 +0100 Subject: [PATCH 3/5] feat: add dedicated UUID filter methods --- .../v1/api/collections/query/Filter.java | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java index a27129b7e..72801c05e 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java @@ -110,8 +110,8 @@ public Filter not() { // -------------------------------------------------------------------------- /** Filter by object UUID. */ - public static FilterBuilder uuid() { - return property(BaseQueryOptions.ID_PROPERTY); + public static UuidProperty uuid() { + return new UuidProperty(); } /** Filter by object creation time. */ @@ -645,6 +645,44 @@ public String toString() { } } + public static class UuidProperty extends PathOperand { + private UuidProperty() { + super(BaseQueryOptions.ID_PROPERTY); + } + + public Filter eq(String value) { + return new Filter(Operator.EQUAL, this, new TextOperand(value)); + } + + public Filter ne(String value) { + return new Filter(Operator.NOT_EQUAL, this, new TextOperand(value)); + } + + public Filter gt(String value) { + return new Filter(Operator.GREATER_THAN, this, new TextOperand(value)); + } + + public Filter gte(String value) { + return new Filter(Operator.GREATER_THAN_EQUAL, this, new TextOperand(value)); + } + + public Filter lt(String value) { + return new Filter(Operator.LESS_THAN, this, new TextOperand(value)); + } + + public Filter lte(String value) { + return new Filter(Operator.LESS_THAN_EQUAL, this, new TextOperand(value)); + } + + public Filter containsAny(String... values) { + return new Filter(Operator.CONTAINS_ANY, this, new TextArrayOperand(values)); + } + + public Filter containsNone(String... values) { + return new Filter(Operator.CONTAINS_NONE, this, new TextArrayOperand(values)); + } + } + public static class DateProperty extends PathOperand { private DateProperty(String propertyName) { super(propertyName); From 6aec8ce9523d88d3ac3f99f56dfe4c04e912e934 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Thu, 20 Nov 2025 15:43:10 +0100 Subject: [PATCH 4/5] fix: remove unimplemented method Filter.references Use new syntax for setting target property --- .../client6/v1/api/collections/query/Filter.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java index 72801c05e..fa146cd15 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java @@ -129,11 +129,6 @@ public static FilterBuilder property(String property) { return new FilterBuilder(new PathOperand(property)); } - /** Filter by a property of the referenced object. */ - public static FilterBuilder reference(String... path) { - return new FilterBuilder(new PathOperand(path)); - } - public static class FilterBuilder { private final FilterOperand left; @@ -632,10 +627,11 @@ private PathOperand(String... path) { @Override public void appendTo(WeaviateProtoBase.Filters.Builder filter) { - // "on" is deprecated, but the current proto doesn't have "path". if (!path.isEmpty()) { - filter.addOn(path.get(0)); + filter.setTarget(WeaviateProtoBase.FilterTarget.newBuilder() + .setProperty(path.get(0))); } + // FIXME: no way to reference objects rn? } From e4d07ade188c8086756933780dc1003c7bc8d3fd Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Thu, 20 Nov 2025 16:08:13 +0100 Subject: [PATCH 5/5] feat: implement filtering by property length --- .../io/weaviate/integration/SearchITest.java | 17 ++++++++++++ .../v1/api/collections/query/Filter.java | 26 +++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/it/java/io/weaviate/integration/SearchITest.java b/src/it/java/io/weaviate/integration/SearchITest.java index 15a4de50e..8893cf222 100644 --- a/src/it/java/io/weaviate/integration/SearchITest.java +++ b/src/it/java/io/weaviate/integration/SearchITest.java @@ -723,4 +723,21 @@ public void test_filterCreateUpdateTime() throws IOException { Assertions.assertThat(beforeNow.objects()).isEmpty(); Assertions.assertThat(afterNow.objects()).hasSize(1); } + + @Test + public void teset_filterPropertyLength() throws IOException { + // Arrange + var nsStrings = ns("Strings"); + + var strings = client.collections.create(nsStrings, c -> c + .invertedIndex(idx -> idx.indexPropertyLength(true)) + .properties(Property.text("letters"))); + strings.data.insertMany(Map.of("letters", "abc"), Map.of("letters", "abcd"), Map.of("letters", "a")); + + // Act + var got = strings.query.fetchObjects(q -> q.filters(Filter.propertyLen("letters").gte(3))); + + // Assertions + Assertions.assertThat(got.objects()).hasSize(2); + } } diff --git a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java index fa146cd15..6a2dc38a0 100644 --- a/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java +++ b/src/main/java/io/weaviate/client6/v1/api/collections/query/Filter.java @@ -126,7 +126,12 @@ public static DateProperty lastUpdatedAt() { /** Filter by object property. */ public static FilterBuilder property(String property) { - return new FilterBuilder(new PathOperand(property)); + return new FilterBuilder(new PathOperand(false, property)); + } + + /** Filter by object property's length. */ + public static FilterBuilder propertyLen(String property) { + return new FilterBuilder(new PathOperand(true, property)); } public static class FilterBuilder { @@ -616,22 +621,27 @@ static FilterOperand fromObject(Object value) { private static class PathOperand implements FilterOperand { private final List path; + private final boolean length; - private PathOperand(List path) { + private PathOperand(boolean length, List path) { this.path = path; + this.length = length; } - private PathOperand(String... path) { - this(Arrays.asList(path)); + private PathOperand(boolean length, String... path) { + this(length, Arrays.asList(path)); } @Override public void appendTo(WeaviateProtoBase.Filters.Builder filter) { if (!path.isEmpty()) { + var property = path.get(0); + if (length) { + property = "len(" + property + ")"; + } filter.setTarget(WeaviateProtoBase.FilterTarget.newBuilder() - .setProperty(path.get(0))); + .setProperty(property)); } - // FIXME: no way to reference objects rn? } @@ -643,7 +653,7 @@ public String toString() { public static class UuidProperty extends PathOperand { private UuidProperty() { - super(BaseQueryOptions.ID_PROPERTY); + super(false, BaseQueryOptions.ID_PROPERTY); } public Filter eq(String value) { @@ -681,7 +691,7 @@ public Filter containsNone(String... values) { public static class DateProperty extends PathOperand { private DateProperty(String propertyName) { - super(propertyName); + super(false, propertyName); } public Filter eq(OffsetDateTime value) {