Skip to content

Commit 3ec6297

Browse files
Add ISNULL/ISNOTNULL filter expression
Signed-off-by: jonghoon park <[email protected]>
1 parent af07517 commit 3ec6297

File tree

17 files changed

+668
-258
lines changed

17 files changed

+668
-258
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
@@ -487,6 +487,25 @@ Consider the following example:
487487
Expression exp = b.and(b.in("genre", "drama", "documentary"), b.not(b.lt("year", 2020))).build();
488488
----
489489

490+
You can also use the following operators:
491+
492+
[source,text]
493+
----
494+
IS: 'IS' | 'is';
495+
NULL: 'NULL' | 'null';
496+
NOT NULL: 'NOT NULL' | 'not null';
497+
----
498+
499+
Consider the following example:
500+
501+
[source,java]
502+
----
503+
Expression exp = b.and(b.isNull("year")).build();
504+
Expression exp = b.and(b.isNotNull("year")).build();
505+
----
506+
507+
NOTE: `IS NULL` and `IS NOT NULL` have not been implemented in all vector stores yet.
508+
490509
== Deleting Documents from Vector Store
491510

492511
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.
@@ -239,6 +239,16 @@ public Filter.Operand visitCompareExpression(FiltersParser.CompareExpressionCont
239239
this.visitIdentifier(ctx.identifier()), this.visit(ctx.constant()));
240240
}
241241

242+
@Override
243+
public Filter.Operand visitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) {
244+
return new Filter.Expression(Filter.ExpressionType.ISNULL, this.visitIdentifier(ctx.identifier()));
245+
}
246+
247+
@Override
248+
public Filter.Operand visitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) {
249+
return new Filter.Expression(Filter.ExpressionType.ISNOTNULL, this.visitIdentifier(ctx.identifier()));
250+
}
251+
242252
private Filter.ExpressionType covertCompare(String compare) {
243253
if (!COMP_EXPRESSION_TYPE_MAP.containsKey(compare)) {
244254
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
@@ -26,6 +26,8 @@ null
2626
null
2727
null
2828
null
29+
null
30+
null
2931

3032
token symbolic names:
3133
null
@@ -49,6 +51,8 @@ OR
4951
IN
5052
NIN
5153
NOT
54+
IS
55+
NULL
5256
BOOLEAN_VALUE
5357
QUOTED_STRING
5458
INTEGER_VALUE
@@ -66,4 +70,4 @@ constant
6670

6771

6872
atn:
69-
[4, 1, 26, 89, 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, 3, 5, 78, 8, 5, 1, 5, 1, 5, 4, 5, 82, 8, 5, 11, 5, 12, 5, 83, 1, 5, 3, 5, 87, 8, 5, 1, 5, 0, 1, 2, 6, 0, 2, 4, 6, 8, 10, 0, 2, 2, 0, 8, 8, 11, 15, 1, 0, 9, 10, 98, 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, 86, 1, 0, 0, 0, 12, 13, 5, 1, 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, 18, 0, 0, 23, 24, 3, 4, 2, 0, 24, 40, 1, 0, 0, 0, 25, 29, 3, 8, 4, 0, 26, 27, 5, 20, 0, 0, 27, 30, 5, 18, 0, 0, 28, 30, 5, 19, 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, 6, 0, 0, 34, 35, 3, 2, 1, 0, 35, 36, 5, 7, 0, 0, 36, 40, 1, 0, 0, 0, 37, 38, 5, 20, 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, 16, 0, 0, 43, 48, 3, 2, 1, 5, 44, 45, 10, 3, 0, 0, 45, 46, 5, 17, 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, 4, 0, 0, 53, 58, 3, 10, 5, 0, 54, 55, 5, 3, 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, 5, 0, 0, 62, 5, 1, 0, 0, 0, 63, 64, 7, 0, 0, 0, 64, 7, 1, 0, 0, 0, 65, 66, 5, 25, 0, 0, 66, 67, 5, 2, 0, 0, 67, 71, 5, 25, 0, 0, 68, 71, 5, 25, 0, 0, 69, 71, 5, 22, 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, 87, 5, 23, 0, 0, 76, 78, 7, 1, 0, 0, 77, 76, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 87, 5, 24, 0, 0, 80, 82, 5, 22, 0, 0, 81, 80, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 81, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 87, 1, 0, 0, 0, 85, 87, 5, 21, 0, 0, 86, 73, 1, 0, 0, 0, 86, 77, 1, 0, 0, 0, 86, 81, 1, 0, 0, 0, 86, 85, 1, 0, 0, 0, 87, 11, 1, 0, 0, 0, 10, 29, 39, 47, 49, 58, 70, 73, 77, 83, 86]
73+
[4, 1, 28, 98, 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, 3, 5, 87, 8, 5, 1, 5, 1, 5, 4, 5, 91, 8, 5, 11, 5, 12, 5, 92, 1, 5, 3, 5, 96, 8, 5, 1, 5, 0, 1, 2, 6, 0, 2, 4, 6, 8, 10, 0, 2, 2, 0, 8, 8, 11, 15, 1, 0, 9, 10, 109, 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, 95, 1, 0, 0, 0, 12, 13, 5, 1, 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, 18, 0, 0, 23, 24, 3, 4, 2, 0, 24, 49, 1, 0, 0, 0, 25, 29, 3, 8, 4, 0, 26, 27, 5, 20, 0, 0, 27, 30, 5, 18, 0, 0, 28, 30, 5, 19, 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, 21, 0, 0, 35, 36, 5, 22, 0, 0, 36, 49, 1, 0, 0, 0, 37, 38, 3, 8, 4, 0, 38, 39, 5, 21, 0, 0, 39, 40, 5, 20, 0, 0, 40, 41, 5, 22, 0, 0, 41, 49, 1, 0, 0, 0, 42, 43, 5, 6, 0, 0, 43, 44, 3, 2, 1, 0, 44, 45, 5, 7, 0, 0, 45, 49, 1, 0, 0, 0, 46, 47, 5, 20, 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, 16, 0, 0, 52, 57, 3, 2, 1, 5, 53, 54, 10, 3, 0, 0, 54, 55, 5, 17, 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, 4, 0, 0, 62, 67, 3, 10, 5, 0, 63, 64, 5, 3, 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, 5, 0, 0, 71, 5, 1, 0, 0, 0, 72, 73, 7, 0, 0, 0, 73, 7, 1, 0, 0, 0, 74, 75, 5, 27, 0, 0, 75, 76, 5, 2, 0, 0, 76, 80, 5, 27, 0, 0, 77, 80, 5, 27, 0, 0, 78, 80, 5, 24, 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, 96, 5, 25, 0, 0, 85, 87, 7, 1, 0, 0, 86, 85, 1, 0, 0, 0, 86, 87, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 96, 5, 26, 0, 0, 89, 91, 5, 24, 0, 0, 90, 89, 1, 0, 0, 0, 91, 92, 1, 0, 0, 0, 92, 90, 1, 0, 0, 0, 92, 93, 1, 0, 0, 0, 93, 96, 1, 0, 0, 0, 94, 96, 5, 23, 0, 0, 95, 82, 1, 0, 0, 0, 95, 86, 1, 0, 0, 0, 95, 90, 1, 0, 0, 0, 95, 94, 1, 0, 0, 0, 96, 11, 1, 0, 0, 0, 10, 29, 48, 56, 58, 67, 79, 82, 86, 92, 95]

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

Lines changed: 45 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.
@@ -78,6 +78,50 @@ public void enterNinExpression(FiltersParser.NinExpressionContext ctx) {
7878
public void exitNinExpression(FiltersParser.NinExpressionContext ctx) {
7979
}
8080

81+
/**
82+
* {@inheritDoc}
83+
*
84+
* <p>
85+
* The default implementation does nothing.
86+
* </p>
87+
*/
88+
@Override
89+
public void enterIsNullExpression(FiltersParser.IsNullExpressionContext ctx) {
90+
}
91+
92+
/**
93+
* {@inheritDoc}
94+
*
95+
* <p>
96+
* The default implementation does nothing.
97+
* </p>
98+
*/
99+
@Override
100+
public void exitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) {
101+
}
102+
103+
/**
104+
* {@inheritDoc}
105+
*
106+
* <p>
107+
* The default implementation does nothing.
108+
* </p>
109+
*/
110+
@Override
111+
public void enterIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) {
112+
}
113+
114+
/**
115+
* {@inheritDoc}
116+
*
117+
* <p>
118+
* The default implementation does nothing.
119+
* </p>
120+
*/
121+
@Override
122+
public void exitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) {
123+
}
124+
81125
/**
82126
* {@inheritDoc}
83127
*

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

Lines changed: 27 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.
@@ -61,6 +61,32 @@ public T visitNinExpression(FiltersParser.NinExpressionContext ctx) {
6161
return visitChildren(ctx);
6262
}
6363

64+
/**
65+
* {@inheritDoc}
66+
*
67+
* <p>
68+
* The default implementation returns the result of calling {@link #visitChildren} on
69+
* {@code ctx}.
70+
* </p>
71+
*/
72+
@Override
73+
public T visitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) {
74+
return visitChildren(ctx);
75+
}
76+
77+
/**
78+
* {@inheritDoc}
79+
*
80+
* <p>
81+
* The default implementation returns the result of calling {@link #visitChildren} on
82+
* {@code ctx}.
83+
* </p>
84+
*/
85+
@Override
86+
public T visitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) {
87+
return visitChildren(ctx);
88+
}
89+
6490
/**
6591
* {@inheritDoc}
6692
*

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

Lines changed: 7 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)