Skip to content

Commit 8e9ad36

Browse files
dev-jonghoonparkericbottard
authored andcommitted
Add ISNULL/ISNOTNULL filter expression
Fix #3694 Signed-off-by: jonghoon park <dev@jonghoonpark.com> Signed-off-by: Eric Bottard <eric.bottard@broadcom.com>
1 parent 7e6da6e commit 8e9ad36

File tree

17 files changed

+716
-305
lines changed

17 files changed

+716
-305
lines changed

spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,25 @@ Consider the following example:
644644
Expression exp = b.and(b.in("genre", "drama", "documentary"), b.not(b.lt("year", 2020))).build();
645645
----
646646

647+
You can also use the following operators:
648+
649+
[source,text]
650+
----
651+
IS: 'IS' | 'is';
652+
NULL: 'NULL' | 'null';
653+
NOT NULL: 'NOT NULL' | 'not null';
654+
----
655+
656+
Consider the following example:
657+
658+
[source,java]
659+
----
660+
Expression exp = b.and(b.isNull("year")).build();
661+
Expression exp = b.and(b.isNotNull("year")).build();
662+
----
663+
664+
NOTE: `IS NULL` and `IS NOT NULL` have not been implemented in all vector stores yet.
665+
647666
== Deleting Documents from Vector Store
648667

649668
The Vector Store interface provides multiple methods for deleting documents, allowing you to remove data either by specific document IDs or using filter expressions.

spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/Filter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -78,7 +78,7 @@ public class Filter {
7878
*/
7979
public enum ExpressionType {
8080

81-
AND, OR, EQ, NE, GT, GTE, LT, LTE, IN, NIN, NOT
81+
AND, OR, EQ, NE, GT, GTE, LT, LTE, IN, NIN, NOT, ISNULL, ISNOTNULL
8282

8383
}
8484

spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionBuilder.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -105,6 +105,14 @@ public Op nin(String key, List<Object> values) {
105105
return new Op(new Filter.Expression(ExpressionType.NIN, new Key(key), new Value(values)));
106106
}
107107

108+
public Op isNull(String key) {
109+
return new Op(new Filter.Expression(ExpressionType.ISNULL, new Key(key)));
110+
}
111+
112+
public Op isNotNull(String key) {
113+
return new Op(new Filter.Expression(ExpressionType.ISNOTNULL, new Key(key)));
114+
}
115+
108116
public Op group(Op content) {
109117
return new Op(new Filter.Group(content.build()));
110118
}

spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionTextParser.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -240,6 +240,16 @@ public Filter.Operand visitCompareExpression(FiltersParser.CompareExpressionCont
240240
this.visitIdentifier(ctx.identifier()), this.visit(ctx.constant()));
241241
}
242242

243+
@Override
244+
public Filter.Operand visitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) {
245+
return new Filter.Expression(Filter.ExpressionType.ISNULL, this.visitIdentifier(ctx.identifier()));
246+
}
247+
248+
@Override
249+
public Filter.Operand visitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) {
250+
return new Filter.Expression(Filter.ExpressionType.ISNOTNULL, this.visitIdentifier(ctx.identifier()));
251+
}
252+
243253
private Filter.ExpressionType covertCompare(String compare) {
244254
if (!COMP_EXPRESSION_TYPE_MAP.containsKey(compare)) {
245255
throw new RuntimeException("Unknown compare operator: " + compare);

spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/Filters.interp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ null
2727
null
2828
null
2929
null
30+
null
31+
null
3032

3133
token symbolic names:
3234
null
@@ -51,6 +53,8 @@ OR
5153
IN
5254
NIN
5355
NOT
56+
IS
57+
NULL
5458
BOOLEAN_VALUE
5559
QUOTED_STRING
5660
INTEGER_VALUE
@@ -68,4 +72,4 @@ constant
6872

6973

7074
atn:
71-
[4, 1, 27, 94, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 30, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 40, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 48, 8, 1, 10, 1, 12, 1, 51, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 5, 2, 57, 8, 2, 10, 2, 12, 2, 60, 9, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 71, 8, 4, 1, 5, 3, 5, 74, 8, 5, 1, 5, 1, 5, 1, 5, 3, 5, 79, 8, 5, 1, 5, 1, 5, 3, 5, 83, 8, 5, 1, 5, 1, 5, 4, 5, 87, 8, 5, 11, 5, 12, 5, 88, 1, 5, 3, 5, 92, 8, 5, 1, 5, 0, 1, 2, 6, 0, 2, 4, 6, 8, 10, 0, 2, 2, 0, 9, 9, 12, 16, 1, 0, 10, 11, 105, 0, 12, 1, 0, 0, 0, 2, 39, 1, 0, 0, 0, 4, 52, 1, 0, 0, 0, 6, 63, 1, 0, 0, 0, 8, 70, 1, 0, 0, 0, 10, 91, 1, 0, 0, 0, 12, 13, 5, 2, 0, 0, 13, 14, 3, 2, 1, 0, 14, 15, 5, 0, 0, 1, 15, 1, 1, 0, 0, 0, 16, 17, 6, 1, -1, 0, 17, 18, 3, 8, 4, 0, 18, 19, 3, 6, 3, 0, 19, 20, 3, 10, 5, 0, 20, 40, 1, 0, 0, 0, 21, 22, 3, 8, 4, 0, 22, 23, 5, 19, 0, 0, 23, 24, 3, 4, 2, 0, 24, 40, 1, 0, 0, 0, 25, 29, 3, 8, 4, 0, 26, 27, 5, 21, 0, 0, 27, 30, 5, 19, 0, 0, 28, 30, 5, 20, 0, 0, 29, 26, 1, 0, 0, 0, 29, 28, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 32, 3, 4, 2, 0, 32, 40, 1, 0, 0, 0, 33, 34, 5, 7, 0, 0, 34, 35, 3, 2, 1, 0, 35, 36, 5, 8, 0, 0, 36, 40, 1, 0, 0, 0, 37, 38, 5, 21, 0, 0, 38, 40, 3, 2, 1, 1, 39, 16, 1, 0, 0, 0, 39, 21, 1, 0, 0, 0, 39, 25, 1, 0, 0, 0, 39, 33, 1, 0, 0, 0, 39, 37, 1, 0, 0, 0, 40, 49, 1, 0, 0, 0, 41, 42, 10, 4, 0, 0, 42, 43, 5, 17, 0, 0, 43, 48, 3, 2, 1, 5, 44, 45, 10, 3, 0, 0, 45, 46, 5, 18, 0, 0, 46, 48, 3, 2, 1, 4, 47, 41, 1, 0, 0, 0, 47, 44, 1, 0, 0, 0, 48, 51, 1, 0, 0, 0, 49, 47, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0, 50, 3, 1, 0, 0, 0, 51, 49, 1, 0, 0, 0, 52, 53, 5, 5, 0, 0, 53, 58, 3, 10, 5, 0, 54, 55, 5, 4, 0, 0, 55, 57, 3, 10, 5, 0, 56, 54, 1, 0, 0, 0, 57, 60, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 61, 1, 0, 0, 0, 60, 58, 1, 0, 0, 0, 61, 62, 5, 6, 0, 0, 62, 5, 1, 0, 0, 0, 63, 64, 7, 0, 0, 0, 64, 7, 1, 0, 0, 0, 65, 66, 5, 26, 0, 0, 66, 67, 5, 3, 0, 0, 67, 71, 5, 26, 0, 0, 68, 71, 5, 26, 0, 0, 69, 71, 5, 23, 0, 0, 70, 65, 1, 0, 0, 0, 70, 68, 1, 0, 0, 0, 70, 69, 1, 0, 0, 0, 71, 9, 1, 0, 0, 0, 72, 74, 7, 1, 0, 0, 73, 72, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 75, 1, 0, 0, 0, 75, 76, 5, 24, 0, 0, 76, 92, 5, 1, 0, 0, 77, 79, 7, 1, 0, 0, 78, 77, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 80, 1, 0, 0, 0, 80, 92, 5, 24, 0, 0, 81, 83, 7, 1, 0, 0, 82, 81, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 92, 5, 25, 0, 0, 85, 87, 5, 23, 0, 0, 86, 85, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 86, 1, 0, 0, 0, 88, 89, 1, 0, 0, 0, 89, 92, 1, 0, 0, 0, 90, 92, 5, 22, 0, 0, 91, 73, 1, 0, 0, 0, 91, 78, 1, 0, 0, 0, 91, 82, 1, 0, 0, 0, 91, 86, 1, 0, 0, 0, 91, 90, 1, 0, 0, 0, 92, 11, 1, 0, 0, 0, 11, 29, 39, 47, 49, 58, 70, 73, 78, 82, 88, 91]
75+
[4, 1, 29, 103, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 30, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 49, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 57, 8, 1, 10, 1, 12, 1, 60, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 5, 2, 66, 8, 2, 10, 2, 12, 2, 69, 9, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 80, 8, 4, 1, 5, 3, 5, 83, 8, 5, 1, 5, 1, 5, 1, 5, 3, 5, 88, 8, 5, 1, 5, 1, 5, 3, 5, 92, 8, 5, 1, 5, 1, 5, 4, 5, 96, 8, 5, 11, 5, 12, 5, 97, 1, 5, 3, 5, 101, 8, 5, 1, 5, 0, 1, 2, 6, 0, 2, 4, 6, 8, 10, 0, 2, 2, 0, 9, 9, 12, 16, 1, 0, 10, 11, 116, 0, 12, 1, 0, 0, 0, 2, 48, 1, 0, 0, 0, 4, 61, 1, 0, 0, 0, 6, 72, 1, 0, 0, 0, 8, 79, 1, 0, 0, 0, 10, 100, 1, 0, 0, 0, 12, 13, 5, 2, 0, 0, 13, 14, 3, 2, 1, 0, 14, 15, 5, 0, 0, 1, 15, 1, 1, 0, 0, 0, 16, 17, 6, 1, -1, 0, 17, 18, 3, 8, 4, 0, 18, 19, 3, 6, 3, 0, 19, 20, 3, 10, 5, 0, 20, 49, 1, 0, 0, 0, 21, 22, 3, 8, 4, 0, 22, 23, 5, 19, 0, 0, 23, 24, 3, 4, 2, 0, 24, 49, 1, 0, 0, 0, 25, 29, 3, 8, 4, 0, 26, 27, 5, 21, 0, 0, 27, 30, 5, 19, 0, 0, 28, 30, 5, 20, 0, 0, 29, 26, 1, 0, 0, 0, 29, 28, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 32, 3, 4, 2, 0, 32, 49, 1, 0, 0, 0, 33, 34, 3, 8, 4, 0, 34, 35, 5, 22, 0, 0, 35, 36, 5, 23, 0, 0, 36, 49, 1, 0, 0, 0, 37, 38, 3, 8, 4, 0, 38, 39, 5, 22, 0, 0, 39, 40, 5, 21, 0, 0, 40, 41, 5, 23, 0, 0, 41, 49, 1, 0, 0, 0, 42, 43, 5, 7, 0, 0, 43, 44, 3, 2, 1, 0, 44, 45, 5, 8, 0, 0, 45, 49, 1, 0, 0, 0, 46, 47, 5, 21, 0, 0, 47, 49, 3, 2, 1, 1, 48, 16, 1, 0, 0, 0, 48, 21, 1, 0, 0, 0, 48, 25, 1, 0, 0, 0, 48, 33, 1, 0, 0, 0, 48, 37, 1, 0, 0, 0, 48, 42, 1, 0, 0, 0, 48, 46, 1, 0, 0, 0, 49, 58, 1, 0, 0, 0, 50, 51, 10, 4, 0, 0, 51, 52, 5, 17, 0, 0, 52, 57, 3, 2, 1, 5, 53, 54, 10, 3, 0, 0, 54, 55, 5, 18, 0, 0, 55, 57, 3, 2, 1, 4, 56, 50, 1, 0, 0, 0, 56, 53, 1, 0, 0, 0, 57, 60, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 3, 1, 0, 0, 0, 60, 58, 1, 0, 0, 0, 61, 62, 5, 5, 0, 0, 62, 67, 3, 10, 5, 0, 63, 64, 5, 4, 0, 0, 64, 66, 3, 10, 5, 0, 65, 63, 1, 0, 0, 0, 66, 69, 1, 0, 0, 0, 67, 65, 1, 0, 0, 0, 67, 68, 1, 0, 0, 0, 68, 70, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 70, 71, 5, 6, 0, 0, 71, 5, 1, 0, 0, 0, 72, 73, 7, 0, 0, 0, 73, 7, 1, 0, 0, 0, 74, 75, 5, 28, 0, 0, 75, 76, 5, 3, 0, 0, 76, 80, 5, 28, 0, 0, 77, 80, 5, 28, 0, 0, 78, 80, 5, 25, 0, 0, 79, 74, 1, 0, 0, 0, 79, 77, 1, 0, 0, 0, 79, 78, 1, 0, 0, 0, 80, 9, 1, 0, 0, 0, 81, 83, 7, 1, 0, 0, 82, 81, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 85, 5, 26, 0, 0, 85, 101, 5, 1, 0, 0, 86, 88, 7, 1, 0, 0, 87, 86, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 89, 1, 0, 0, 0, 89, 101, 5, 26, 0, 0, 90, 92, 7, 1, 0, 0, 91, 90, 1, 0, 0, 0, 91, 92, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 101, 5, 27, 0, 0, 94, 96, 5, 25, 0, 0, 95, 94, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 95, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 101, 1, 0, 0, 0, 99, 101, 5, 24, 0, 0, 100, 82, 1, 0, 0, 0, 100, 87, 1, 0, 0, 0, 100, 91, 1, 0, 0, 0, 100, 95, 1, 0, 0, 0, 100, 99, 1, 0, 0, 0, 101, 11, 1, 0, 0, 0, 11, 29, 48, 56, 58, 67, 79, 82, 87, 91, 97, 100]

spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseListener.java

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
// Generated from org/springframework/ai/vectorstore/filter/antlr4/Filters.g4 by ANTLR 4.13.1
2+
package org.springframework.ai.vectorstore.filter.antlr4;
3+
14
/*
2-
* Copyright 2023-2024 the original author or authors.
5+
* Copyright 2023-2023 the original author or authors.
36
*
47
* Licensed under the Apache License, Version 2.0 (the "License");
58
* you may not use this file except in compliance with the License.
@@ -14,10 +17,6 @@
1417
* limitations under the License.
1518
*/
1619

17-
package org.springframework.ai.vectorstore.filter.antlr4;
18-
19-
// Generated from org/springframework/ai/vectorstore/filter/antlr4/Filters.g4 by ANTLR 4.13.1
20-
2120
// ############################################################
2221
// # NOTE: This is ANTLR4 auto-generated code. Do not modify! #
2322
// ############################################################
@@ -78,6 +77,50 @@ public void enterNinExpression(FiltersParser.NinExpressionContext ctx) {
7877
public void exitNinExpression(FiltersParser.NinExpressionContext ctx) {
7978
}
8079

80+
/**
81+
* {@inheritDoc}
82+
*
83+
* <p>
84+
* The default implementation does nothing.
85+
* </p>
86+
*/
87+
@Override
88+
public void enterIsNullExpression(FiltersParser.IsNullExpressionContext ctx) {
89+
}
90+
91+
/**
92+
* {@inheritDoc}
93+
*
94+
* <p>
95+
* The default implementation does nothing.
96+
* </p>
97+
*/
98+
@Override
99+
public void exitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) {
100+
}
101+
102+
/**
103+
* {@inheritDoc}
104+
*
105+
* <p>
106+
* The default implementation does nothing.
107+
* </p>
108+
*/
109+
@Override
110+
public void enterIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) {
111+
}
112+
113+
/**
114+
* {@inheritDoc}
115+
*
116+
* <p>
117+
* The default implementation does nothing.
118+
* </p>
119+
*/
120+
@Override
121+
public void exitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) {
122+
}
123+
81124
/**
82125
* {@inheritDoc}
83126
*
@@ -430,4 +473,4 @@ public void visitTerminal(TerminalNode node) {
430473
public void visitErrorNode(ErrorNode node) {
431474
}
432475

433-
}
476+
}

spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseVisitor.java

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
// Generated from org/springframework/ai/vectorstore/filter/antlr4/Filters.g4 by ANTLR 4.13.1
2+
package org.springframework.ai.vectorstore.filter.antlr4;
3+
14
/*
2-
* Copyright 2023-2024 the original author or authors.
5+
* Copyright 2023-2023 the original author or authors.
36
*
47
* Licensed under the Apache License, Version 2.0 (the "License");
58
* you may not use this file except in compliance with the License.
@@ -14,10 +17,6 @@
1417
* limitations under the License.
1518
*/
1619

17-
package org.springframework.ai.vectorstore.filter.antlr4;
18-
19-
// Generated from org/springframework/ai/vectorstore/filter/antlr4/Filters.g4 by ANTLR 4.13.1
20-
2120
// ############################################################
2221
// # NOTE: This is ANTLR4 auto-generated code. Do not modify! #
2322
// ############################################################
@@ -61,6 +60,32 @@ public T visitNinExpression(FiltersParser.NinExpressionContext ctx) {
6160
return visitChildren(ctx);
6261
}
6362

63+
/**
64+
* {@inheritDoc}
65+
*
66+
* <p>
67+
* The default implementation returns the result of calling {@link #visitChildren} on
68+
* {@code ctx}.
69+
* </p>
70+
*/
71+
@Override
72+
public T visitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) {
73+
return visitChildren(ctx);
74+
}
75+
76+
/**
77+
* {@inheritDoc}
78+
*
79+
* <p>
80+
* The default implementation returns the result of calling {@link #visitChildren} on
81+
* {@code ctx}.
82+
* </p>
83+
*/
84+
@Override
85+
public T visitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) {
86+
return visitChildren(ctx);
87+
}
88+
6489
/**
6590
* {@inheritDoc}
6691
*
@@ -243,4 +268,4 @@ public T visitBooleanConstant(FiltersParser.BooleanConstantContext ctx) {
243268
return visitChildren(ctx);
244269
}
245270

246-
}
271+
}

0 commit comments

Comments
 (0)