From 926a6e6c9a71395e7ab296c39ecc8d8c26142aaa Mon Sep 17 00:00:00 2001 From: jonghoon park Date: Mon, 30 Jun 2025 21:16:14 +0900 Subject: [PATCH] Add ISNULL/ISNOTNULL filter expression Signed-off-by: jonghoon park --- .../modules/ROOT/pages/api/vectordbs.adoc | 19 ++ .../ai/vectorstore/filter/Filter.java | 4 +- .../filter/FilterExpressionBuilder.java | 10 +- .../filter/FilterExpressionTextParser.java | 12 +- .../vectorstore/filter/antlr4/Filters.interp | 6 +- .../filter/antlr4/FiltersBaseListener.java | 46 ++- .../filter/antlr4/FiltersBaseVisitor.java | 28 +- .../filter/antlr4/FiltersLexer.interp | 8 +- .../filter/antlr4/FiltersLexer.java | 312 ++++++++--------- .../filter/antlr4/FiltersListener.java | 30 +- .../filter/antlr4/FiltersParser.java | 315 ++++++++++++------ .../filter/antlr4/FiltersVisitor.java | 18 +- .../AbstractFilterExpressionConverter.java | 5 +- .../ai/vectorstore/filter/antlr4/Filters.g4 | 6 +- .../CoherenceFilterExpressionConverter.java | 3 +- ...archAiSearchFilterExpressionConverter.java | 9 + .../ElasticsearchVectorStoreIT.java | 94 ++++++ 17 files changed, 667 insertions(+), 258 deletions(-) diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc index 851456dd75d..499f9c5b6f6 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc @@ -487,6 +487,25 @@ Consider the following example: Expression exp = b.and(b.in("genre", "drama", "documentary"), b.not(b.lt("year", 2020))).build(); ---- +You can also use the following operators: + +[source,text] +---- +IS: 'IS' | 'is'; +NULL: 'NULL' | 'null'; +NOT NULL: 'NOT NULL' | 'not null'; +---- + +Consider the following example: + +[source,java] +---- +Expression exp = b.and(b.isNull("year")).build(); +Expression exp = b.and(b.isNotNull("year")).build(); +---- + +NOTE: `IS NULL` and `IS NOT NULL` have not been implemented in all vector stores yet. + == Deleting Documents from Vector Store The Vector Store interface provides multiple methods for deleting documents, allowing you to remove data either by specific document IDs or using filter expressions. diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/Filter.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/Filter.java index 53e16c691d4..a4dbd7be9b0 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/Filter.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/Filter.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,7 +78,7 @@ public class Filter { */ public enum ExpressionType { - AND, OR, EQ, NE, GT, GTE, LT, LTE, IN, NIN, NOT + AND, OR, EQ, NE, GT, GTE, LT, LTE, IN, NIN, NOT, ISNULL, ISNOTNULL } diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionBuilder.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionBuilder.java index d3cb3c7c486..a9b01df12b7 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionBuilder.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,6 +105,14 @@ public Op nin(String key, List values) { return new Op(new Filter.Expression(ExpressionType.NIN, new Key(key), new Value(values))); } + public Op isNull(String key) { + return new Op(new Filter.Expression(ExpressionType.ISNULL, new Key(key))); + } + + public Op isNotNull(String key) { + return new Op(new Filter.Expression(ExpressionType.ISNOTNULL, new Key(key))); + } + public Op group(Op content) { return new Op(new Filter.Group(content.build())); } diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionTextParser.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionTextParser.java index a36f70c4053..50ca4376274 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionTextParser.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/FilterExpressionTextParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -239,6 +239,16 @@ public Filter.Operand visitCompareExpression(FiltersParser.CompareExpressionCont this.visitIdentifier(ctx.identifier()), this.visit(ctx.constant())); } + @Override + public Filter.Operand visitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) { + return new Filter.Expression(Filter.ExpressionType.ISNULL, this.visitIdentifier(ctx.identifier())); + } + + @Override + public Filter.Operand visitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) { + return new Filter.Expression(Filter.ExpressionType.ISNOTNULL, this.visitIdentifier(ctx.identifier())); + } + private Filter.ExpressionType covertCompare(String compare) { if (!COMP_EXPRESSION_TYPE_MAP.containsKey(compare)) { throw new RuntimeException("Unknown compare operator: " + compare); diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/Filters.interp b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/Filters.interp index 51775a8a55c..6b4c3108cce 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/Filters.interp +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/Filters.interp @@ -26,6 +26,8 @@ null null null null +null +null token symbolic names: null @@ -49,6 +51,8 @@ OR IN NIN NOT +IS +NULL BOOLEAN_VALUE QUOTED_STRING INTEGER_VALUE @@ -66,4 +70,4 @@ constant atn: -[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] \ No newline at end of file +[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] \ No newline at end of file diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseListener.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseListener.java index 136aedbab1b..187827b4952 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseListener.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,6 +78,50 @@ public void enterNinExpression(FiltersParser.NinExpressionContext ctx) { public void exitNinExpression(FiltersParser.NinExpressionContext ctx) { } + /** + * {@inheritDoc} + * + *

+ * The default implementation does nothing. + *

+ */ + @Override + public void enterIsNullExpression(FiltersParser.IsNullExpressionContext ctx) { + } + + /** + * {@inheritDoc} + * + *

+ * The default implementation does nothing. + *

+ */ + @Override + public void exitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) { + } + + /** + * {@inheritDoc} + * + *

+ * The default implementation does nothing. + *

+ */ + @Override + public void enterIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) { + } + + /** + * {@inheritDoc} + * + *

+ * The default implementation does nothing. + *

+ */ + @Override + public void exitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) { + } + /** * {@inheritDoc} * diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseVisitor.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseVisitor.java index f8a5a204199..7ec150987da 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseVisitor.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersBaseVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +61,32 @@ public T visitNinExpression(FiltersParser.NinExpressionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

+ * The default implementation returns the result of calling {@link #visitChildren} on + * {@code ctx}. + *

+ */ + @Override + public T visitIsNullExpression(FiltersParser.IsNullExpressionContext ctx) { + return visitChildren(ctx); + } + + /** + * {@inheritDoc} + * + *

+ * The default implementation returns the result of calling {@link #visitChildren} on + * {@code ctx}. + *

+ */ + @Override + public T visitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx) { + return visitChildren(ctx); + } + /** * {@inheritDoc} * diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersLexer.interp b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersLexer.interp index 919669898cb..df2559d57f4 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersLexer.interp +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersLexer.interp @@ -26,6 +26,8 @@ null null null null +null +null token symbolic names: null @@ -49,6 +51,8 @@ OR IN NIN NOT +IS +NULL BOOLEAN_VALUE QUOTED_STRING INTEGER_VALUE @@ -77,6 +81,8 @@ OR IN NIN NOT +IS +NULL BOOLEAN_VALUE QUOTED_STRING INTEGER_VALUE @@ -95,4 +101,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 26, 230, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 70, 8, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 112, 8, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 3, 16, 120, 8, 16, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 126, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 3, 18, 134, 8, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 142, 8, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 162, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 168, 8, 21, 10, 21, 12, 21, 171, 9, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 178, 8, 21, 10, 21, 12, 21, 181, 9, 21, 1, 21, 3, 21, 184, 8, 21, 1, 22, 4, 22, 187, 8, 22, 11, 22, 12, 22, 188, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 4, 24, 196, 8, 24, 11, 24, 12, 24, 197, 1, 25, 4, 25, 201, 8, 25, 11, 25, 12, 25, 202, 1, 25, 1, 25, 5, 25, 207, 8, 25, 10, 25, 12, 25, 210, 9, 25, 1, 25, 1, 25, 4, 25, 214, 8, 25, 11, 25, 12, 25, 215, 3, 25, 218, 8, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 4, 28, 225, 8, 28, 11, 28, 12, 28, 226, 1, 28, 1, 28, 0, 0, 29, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 0, 53, 0, 55, 0, 57, 26, 1, 0, 5, 2, 0, 39, 39, 92, 92, 2, 0, 34, 34, 92, 92, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 251, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 1, 69, 1, 0, 0, 0, 3, 71, 1, 0, 0, 0, 5, 73, 1, 0, 0, 0, 7, 75, 1, 0, 0, 0, 9, 77, 1, 0, 0, 0, 11, 79, 1, 0, 0, 0, 13, 81, 1, 0, 0, 0, 15, 83, 1, 0, 0, 0, 17, 86, 1, 0, 0, 0, 19, 88, 1, 0, 0, 0, 21, 90, 1, 0, 0, 0, 23, 92, 1, 0, 0, 0, 25, 95, 1, 0, 0, 0, 27, 97, 1, 0, 0, 0, 29, 100, 1, 0, 0, 0, 31, 111, 1, 0, 0, 0, 33, 119, 1, 0, 0, 0, 35, 125, 1, 0, 0, 0, 37, 133, 1, 0, 0, 0, 39, 141, 1, 0, 0, 0, 41, 161, 1, 0, 0, 0, 43, 183, 1, 0, 0, 0, 45, 186, 1, 0, 0, 0, 47, 190, 1, 0, 0, 0, 49, 195, 1, 0, 0, 0, 51, 217, 1, 0, 0, 0, 53, 219, 1, 0, 0, 0, 55, 221, 1, 0, 0, 0, 57, 224, 1, 0, 0, 0, 59, 60, 5, 87, 0, 0, 60, 61, 5, 72, 0, 0, 61, 62, 5, 69, 0, 0, 62, 63, 5, 82, 0, 0, 63, 70, 5, 69, 0, 0, 64, 65, 5, 119, 0, 0, 65, 66, 5, 104, 0, 0, 66, 67, 5, 101, 0, 0, 67, 68, 5, 114, 0, 0, 68, 70, 5, 101, 0, 0, 69, 59, 1, 0, 0, 0, 69, 64, 1, 0, 0, 0, 70, 2, 1, 0, 0, 0, 71, 72, 5, 46, 0, 0, 72, 4, 1, 0, 0, 0, 73, 74, 5, 44, 0, 0, 74, 6, 1, 0, 0, 0, 75, 76, 5, 91, 0, 0, 76, 8, 1, 0, 0, 0, 77, 78, 5, 93, 0, 0, 78, 10, 1, 0, 0, 0, 79, 80, 5, 40, 0, 0, 80, 12, 1, 0, 0, 0, 81, 82, 5, 41, 0, 0, 82, 14, 1, 0, 0, 0, 83, 84, 5, 61, 0, 0, 84, 85, 5, 61, 0, 0, 85, 16, 1, 0, 0, 0, 86, 87, 5, 45, 0, 0, 87, 18, 1, 0, 0, 0, 88, 89, 5, 43, 0, 0, 89, 20, 1, 0, 0, 0, 90, 91, 5, 62, 0, 0, 91, 22, 1, 0, 0, 0, 92, 93, 5, 62, 0, 0, 93, 94, 5, 61, 0, 0, 94, 24, 1, 0, 0, 0, 95, 96, 5, 60, 0, 0, 96, 26, 1, 0, 0, 0, 97, 98, 5, 60, 0, 0, 98, 99, 5, 61, 0, 0, 99, 28, 1, 0, 0, 0, 100, 101, 5, 33, 0, 0, 101, 102, 5, 61, 0, 0, 102, 30, 1, 0, 0, 0, 103, 104, 5, 65, 0, 0, 104, 105, 5, 78, 0, 0, 105, 112, 5, 68, 0, 0, 106, 107, 5, 97, 0, 0, 107, 108, 5, 110, 0, 0, 108, 112, 5, 100, 0, 0, 109, 110, 5, 38, 0, 0, 110, 112, 5, 38, 0, 0, 111, 103, 1, 0, 0, 0, 111, 106, 1, 0, 0, 0, 111, 109, 1, 0, 0, 0, 112, 32, 1, 0, 0, 0, 113, 114, 5, 79, 0, 0, 114, 120, 5, 82, 0, 0, 115, 116, 5, 111, 0, 0, 116, 120, 5, 114, 0, 0, 117, 118, 5, 124, 0, 0, 118, 120, 5, 124, 0, 0, 119, 113, 1, 0, 0, 0, 119, 115, 1, 0, 0, 0, 119, 117, 1, 0, 0, 0, 120, 34, 1, 0, 0, 0, 121, 122, 5, 73, 0, 0, 122, 126, 5, 78, 0, 0, 123, 124, 5, 105, 0, 0, 124, 126, 5, 110, 0, 0, 125, 121, 1, 0, 0, 0, 125, 123, 1, 0, 0, 0, 126, 36, 1, 0, 0, 0, 127, 128, 5, 78, 0, 0, 128, 129, 5, 73, 0, 0, 129, 134, 5, 78, 0, 0, 130, 131, 5, 110, 0, 0, 131, 132, 5, 105, 0, 0, 132, 134, 5, 110, 0, 0, 133, 127, 1, 0, 0, 0, 133, 130, 1, 0, 0, 0, 134, 38, 1, 0, 0, 0, 135, 136, 5, 78, 0, 0, 136, 137, 5, 79, 0, 0, 137, 142, 5, 84, 0, 0, 138, 139, 5, 110, 0, 0, 139, 140, 5, 111, 0, 0, 140, 142, 5, 116, 0, 0, 141, 135, 1, 0, 0, 0, 141, 138, 1, 0, 0, 0, 142, 40, 1, 0, 0, 0, 143, 144, 5, 84, 0, 0, 144, 145, 5, 82, 0, 0, 145, 146, 5, 85, 0, 0, 146, 162, 5, 69, 0, 0, 147, 148, 5, 116, 0, 0, 148, 149, 5, 114, 0, 0, 149, 150, 5, 117, 0, 0, 150, 162, 5, 101, 0, 0, 151, 152, 5, 70, 0, 0, 152, 153, 5, 65, 0, 0, 153, 154, 5, 76, 0, 0, 154, 155, 5, 83, 0, 0, 155, 162, 5, 69, 0, 0, 156, 157, 5, 102, 0, 0, 157, 158, 5, 97, 0, 0, 158, 159, 5, 108, 0, 0, 159, 160, 5, 115, 0, 0, 160, 162, 5, 101, 0, 0, 161, 143, 1, 0, 0, 0, 161, 147, 1, 0, 0, 0, 161, 151, 1, 0, 0, 0, 161, 156, 1, 0, 0, 0, 162, 42, 1, 0, 0, 0, 163, 169, 5, 39, 0, 0, 164, 168, 8, 0, 0, 0, 165, 166, 5, 92, 0, 0, 166, 168, 9, 0, 0, 0, 167, 164, 1, 0, 0, 0, 167, 165, 1, 0, 0, 0, 168, 171, 1, 0, 0, 0, 169, 167, 1, 0, 0, 0, 169, 170, 1, 0, 0, 0, 170, 172, 1, 0, 0, 0, 171, 169, 1, 0, 0, 0, 172, 184, 5, 39, 0, 0, 173, 179, 5, 34, 0, 0, 174, 178, 8, 1, 0, 0, 175, 176, 5, 92, 0, 0, 176, 178, 9, 0, 0, 0, 177, 174, 1, 0, 0, 0, 177, 175, 1, 0, 0, 0, 178, 181, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 179, 180, 1, 0, 0, 0, 180, 182, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 182, 184, 5, 34, 0, 0, 183, 163, 1, 0, 0, 0, 183, 173, 1, 0, 0, 0, 184, 44, 1, 0, 0, 0, 185, 187, 3, 53, 26, 0, 186, 185, 1, 0, 0, 0, 187, 188, 1, 0, 0, 0, 188, 186, 1, 0, 0, 0, 188, 189, 1, 0, 0, 0, 189, 46, 1, 0, 0, 0, 190, 191, 3, 51, 25, 0, 191, 48, 1, 0, 0, 0, 192, 196, 3, 55, 27, 0, 193, 196, 3, 53, 26, 0, 194, 196, 5, 95, 0, 0, 195, 192, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 195, 194, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 197, 198, 1, 0, 0, 0, 198, 50, 1, 0, 0, 0, 199, 201, 3, 53, 26, 0, 200, 199, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0, 202, 203, 1, 0, 0, 0, 203, 204, 1, 0, 0, 0, 204, 208, 5, 46, 0, 0, 205, 207, 3, 53, 26, 0, 206, 205, 1, 0, 0, 0, 207, 210, 1, 0, 0, 0, 208, 206, 1, 0, 0, 0, 208, 209, 1, 0, 0, 0, 209, 218, 1, 0, 0, 0, 210, 208, 1, 0, 0, 0, 211, 213, 5, 46, 0, 0, 212, 214, 3, 53, 26, 0, 213, 212, 1, 0, 0, 0, 214, 215, 1, 0, 0, 0, 215, 213, 1, 0, 0, 0, 215, 216, 1, 0, 0, 0, 216, 218, 1, 0, 0, 0, 217, 200, 1, 0, 0, 0, 217, 211, 1, 0, 0, 0, 218, 52, 1, 0, 0, 0, 219, 220, 7, 2, 0, 0, 220, 54, 1, 0, 0, 0, 221, 222, 7, 3, 0, 0, 222, 56, 1, 0, 0, 0, 223, 225, 7, 4, 0, 0, 224, 223, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 224, 1, 0, 0, 0, 226, 227, 1, 0, 0, 0, 227, 228, 1, 0, 0, 0, 228, 229, 6, 28, 0, 0, 229, 58, 1, 0, 0, 0, 21, 0, 69, 111, 119, 125, 133, 141, 161, 167, 169, 177, 179, 183, 188, 195, 197, 202, 208, 215, 217, 226, 1, 0, 1, 0] \ No newline at end of file +[4, 0, 28, 250, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 74, 8, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 116, 8, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 3, 16, 124, 8, 16, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 130, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 3, 18, 138, 8, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 146, 8, 19, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 152, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 162, 8, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 182, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 5, 23, 188, 8, 23, 10, 23, 12, 23, 191, 9, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 5, 23, 198, 8, 23, 10, 23, 12, 23, 201, 9, 23, 1, 23, 3, 23, 204, 8, 23, 1, 24, 4, 24, 207, 8, 24, 11, 24, 12, 24, 208, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 4, 26, 216, 8, 26, 11, 26, 12, 26, 217, 1, 27, 4, 27, 221, 8, 27, 11, 27, 12, 27, 222, 1, 27, 1, 27, 5, 27, 227, 8, 27, 10, 27, 12, 27, 230, 9, 27, 1, 27, 1, 27, 4, 27, 234, 8, 27, 11, 27, 12, 27, 235, 3, 27, 238, 8, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 4, 30, 245, 8, 30, 11, 30, 12, 30, 246, 1, 30, 1, 30, 0, 0, 31, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 0, 57, 0, 59, 0, 61, 28, 1, 0, 5, 2, 0, 39, 39, 92, 92, 2, 0, 34, 34, 92, 92, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 273, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 1, 73, 1, 0, 0, 0, 3, 75, 1, 0, 0, 0, 5, 77, 1, 0, 0, 0, 7, 79, 1, 0, 0, 0, 9, 81, 1, 0, 0, 0, 11, 83, 1, 0, 0, 0, 13, 85, 1, 0, 0, 0, 15, 87, 1, 0, 0, 0, 17, 90, 1, 0, 0, 0, 19, 92, 1, 0, 0, 0, 21, 94, 1, 0, 0, 0, 23, 96, 1, 0, 0, 0, 25, 99, 1, 0, 0, 0, 27, 101, 1, 0, 0, 0, 29, 104, 1, 0, 0, 0, 31, 115, 1, 0, 0, 0, 33, 123, 1, 0, 0, 0, 35, 129, 1, 0, 0, 0, 37, 137, 1, 0, 0, 0, 39, 145, 1, 0, 0, 0, 41, 151, 1, 0, 0, 0, 43, 161, 1, 0, 0, 0, 45, 181, 1, 0, 0, 0, 47, 203, 1, 0, 0, 0, 49, 206, 1, 0, 0, 0, 51, 210, 1, 0, 0, 0, 53, 215, 1, 0, 0, 0, 55, 237, 1, 0, 0, 0, 57, 239, 1, 0, 0, 0, 59, 241, 1, 0, 0, 0, 61, 244, 1, 0, 0, 0, 63, 64, 5, 87, 0, 0, 64, 65, 5, 72, 0, 0, 65, 66, 5, 69, 0, 0, 66, 67, 5, 82, 0, 0, 67, 74, 5, 69, 0, 0, 68, 69, 5, 119, 0, 0, 69, 70, 5, 104, 0, 0, 70, 71, 5, 101, 0, 0, 71, 72, 5, 114, 0, 0, 72, 74, 5, 101, 0, 0, 73, 63, 1, 0, 0, 0, 73, 68, 1, 0, 0, 0, 74, 2, 1, 0, 0, 0, 75, 76, 5, 46, 0, 0, 76, 4, 1, 0, 0, 0, 77, 78, 5, 44, 0, 0, 78, 6, 1, 0, 0, 0, 79, 80, 5, 91, 0, 0, 80, 8, 1, 0, 0, 0, 81, 82, 5, 93, 0, 0, 82, 10, 1, 0, 0, 0, 83, 84, 5, 40, 0, 0, 84, 12, 1, 0, 0, 0, 85, 86, 5, 41, 0, 0, 86, 14, 1, 0, 0, 0, 87, 88, 5, 61, 0, 0, 88, 89, 5, 61, 0, 0, 89, 16, 1, 0, 0, 0, 90, 91, 5, 45, 0, 0, 91, 18, 1, 0, 0, 0, 92, 93, 5, 43, 0, 0, 93, 20, 1, 0, 0, 0, 94, 95, 5, 62, 0, 0, 95, 22, 1, 0, 0, 0, 96, 97, 5, 62, 0, 0, 97, 98, 5, 61, 0, 0, 98, 24, 1, 0, 0, 0, 99, 100, 5, 60, 0, 0, 100, 26, 1, 0, 0, 0, 101, 102, 5, 60, 0, 0, 102, 103, 5, 61, 0, 0, 103, 28, 1, 0, 0, 0, 104, 105, 5, 33, 0, 0, 105, 106, 5, 61, 0, 0, 106, 30, 1, 0, 0, 0, 107, 108, 5, 65, 0, 0, 108, 109, 5, 78, 0, 0, 109, 116, 5, 68, 0, 0, 110, 111, 5, 97, 0, 0, 111, 112, 5, 110, 0, 0, 112, 116, 5, 100, 0, 0, 113, 114, 5, 38, 0, 0, 114, 116, 5, 38, 0, 0, 115, 107, 1, 0, 0, 0, 115, 110, 1, 0, 0, 0, 115, 113, 1, 0, 0, 0, 116, 32, 1, 0, 0, 0, 117, 118, 5, 79, 0, 0, 118, 124, 5, 82, 0, 0, 119, 120, 5, 111, 0, 0, 120, 124, 5, 114, 0, 0, 121, 122, 5, 124, 0, 0, 122, 124, 5, 124, 0, 0, 123, 117, 1, 0, 0, 0, 123, 119, 1, 0, 0, 0, 123, 121, 1, 0, 0, 0, 124, 34, 1, 0, 0, 0, 125, 126, 5, 73, 0, 0, 126, 130, 5, 78, 0, 0, 127, 128, 5, 105, 0, 0, 128, 130, 5, 110, 0, 0, 129, 125, 1, 0, 0, 0, 129, 127, 1, 0, 0, 0, 130, 36, 1, 0, 0, 0, 131, 132, 5, 78, 0, 0, 132, 133, 5, 73, 0, 0, 133, 138, 5, 78, 0, 0, 134, 135, 5, 110, 0, 0, 135, 136, 5, 105, 0, 0, 136, 138, 5, 110, 0, 0, 137, 131, 1, 0, 0, 0, 137, 134, 1, 0, 0, 0, 138, 38, 1, 0, 0, 0, 139, 140, 5, 78, 0, 0, 140, 141, 5, 79, 0, 0, 141, 146, 5, 84, 0, 0, 142, 143, 5, 110, 0, 0, 143, 144, 5, 111, 0, 0, 144, 146, 5, 116, 0, 0, 145, 139, 1, 0, 0, 0, 145, 142, 1, 0, 0, 0, 146, 40, 1, 0, 0, 0, 147, 148, 5, 73, 0, 0, 148, 152, 5, 83, 0, 0, 149, 150, 5, 105, 0, 0, 150, 152, 5, 115, 0, 0, 151, 147, 1, 0, 0, 0, 151, 149, 1, 0, 0, 0, 152, 42, 1, 0, 0, 0, 153, 154, 5, 78, 0, 0, 154, 155, 5, 85, 0, 0, 155, 156, 5, 76, 0, 0, 156, 162, 5, 76, 0, 0, 157, 158, 5, 110, 0, 0, 158, 159, 5, 117, 0, 0, 159, 160, 5, 108, 0, 0, 160, 162, 5, 108, 0, 0, 161, 153, 1, 0, 0, 0, 161, 157, 1, 0, 0, 0, 162, 44, 1, 0, 0, 0, 163, 164, 5, 84, 0, 0, 164, 165, 5, 82, 0, 0, 165, 166, 5, 85, 0, 0, 166, 182, 5, 69, 0, 0, 167, 168, 5, 116, 0, 0, 168, 169, 5, 114, 0, 0, 169, 170, 5, 117, 0, 0, 170, 182, 5, 101, 0, 0, 171, 172, 5, 70, 0, 0, 172, 173, 5, 65, 0, 0, 173, 174, 5, 76, 0, 0, 174, 175, 5, 83, 0, 0, 175, 182, 5, 69, 0, 0, 176, 177, 5, 102, 0, 0, 177, 178, 5, 97, 0, 0, 178, 179, 5, 108, 0, 0, 179, 180, 5, 115, 0, 0, 180, 182, 5, 101, 0, 0, 181, 163, 1, 0, 0, 0, 181, 167, 1, 0, 0, 0, 181, 171, 1, 0, 0, 0, 181, 176, 1, 0, 0, 0, 182, 46, 1, 0, 0, 0, 183, 189, 5, 39, 0, 0, 184, 188, 8, 0, 0, 0, 185, 186, 5, 92, 0, 0, 186, 188, 9, 0, 0, 0, 187, 184, 1, 0, 0, 0, 187, 185, 1, 0, 0, 0, 188, 191, 1, 0, 0, 0, 189, 187, 1, 0, 0, 0, 189, 190, 1, 0, 0, 0, 190, 192, 1, 0, 0, 0, 191, 189, 1, 0, 0, 0, 192, 204, 5, 39, 0, 0, 193, 199, 5, 34, 0, 0, 194, 198, 8, 1, 0, 0, 195, 196, 5, 92, 0, 0, 196, 198, 9, 0, 0, 0, 197, 194, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 201, 1, 0, 0, 0, 199, 197, 1, 0, 0, 0, 199, 200, 1, 0, 0, 0, 200, 202, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 202, 204, 5, 34, 0, 0, 203, 183, 1, 0, 0, 0, 203, 193, 1, 0, 0, 0, 204, 48, 1, 0, 0, 0, 205, 207, 3, 57, 28, 0, 206, 205, 1, 0, 0, 0, 207, 208, 1, 0, 0, 0, 208, 206, 1, 0, 0, 0, 208, 209, 1, 0, 0, 0, 209, 50, 1, 0, 0, 0, 210, 211, 3, 55, 27, 0, 211, 52, 1, 0, 0, 0, 212, 216, 3, 59, 29, 0, 213, 216, 3, 57, 28, 0, 214, 216, 5, 95, 0, 0, 215, 212, 1, 0, 0, 0, 215, 213, 1, 0, 0, 0, 215, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 215, 1, 0, 0, 0, 217, 218, 1, 0, 0, 0, 218, 54, 1, 0, 0, 0, 219, 221, 3, 57, 28, 0, 220, 219, 1, 0, 0, 0, 221, 222, 1, 0, 0, 0, 222, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 224, 1, 0, 0, 0, 224, 228, 5, 46, 0, 0, 225, 227, 3, 57, 28, 0, 226, 225, 1, 0, 0, 0, 227, 230, 1, 0, 0, 0, 228, 226, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 238, 1, 0, 0, 0, 230, 228, 1, 0, 0, 0, 231, 233, 5, 46, 0, 0, 232, 234, 3, 57, 28, 0, 233, 232, 1, 0, 0, 0, 234, 235, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 235, 236, 1, 0, 0, 0, 236, 238, 1, 0, 0, 0, 237, 220, 1, 0, 0, 0, 237, 231, 1, 0, 0, 0, 238, 56, 1, 0, 0, 0, 239, 240, 7, 2, 0, 0, 240, 58, 1, 0, 0, 0, 241, 242, 7, 3, 0, 0, 242, 60, 1, 0, 0, 0, 243, 245, 7, 4, 0, 0, 244, 243, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 244, 1, 0, 0, 0, 246, 247, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 249, 6, 30, 0, 0, 249, 62, 1, 0, 0, 0, 23, 0, 73, 115, 123, 129, 137, 145, 151, 161, 181, 187, 189, 197, 199, 203, 208, 215, 217, 222, 228, 235, 237, 246, 1, 0, 1, 0] \ No newline at end of file diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersLexer.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersLexer.java index dc1491d5488..af420913850 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersLexer.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersLexer.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,8 +38,8 @@ public class FiltersLexer extends Lexer { public static final int WHERE = 1, DOT = 2, COMMA = 3, LEFT_SQUARE_BRACKETS = 4, RIGHT_SQUARE_BRACKETS = 5, LEFT_PARENTHESIS = 6, RIGHT_PARENTHESIS = 7, EQUALS = 8, MINUS = 9, PLUS = 10, GT = 11, GE = 12, LT = 13, - LE = 14, NE = 15, AND = 16, OR = 17, IN = 18, NIN = 19, NOT = 20, BOOLEAN_VALUE = 21, QUOTED_STRING = 22, - INTEGER_VALUE = 23, DECIMAL_VALUE = 24, IDENTIFIER = 25, WS = 26; + LE = 14, NE = 15, AND = 16, OR = 17, IN = 18, NIN = 19, NOT = 20, IS = 21, NULL = 22, BOOLEAN_VALUE = 23, + QUOTED_STRING = 24, INTEGER_VALUE = 25, DECIMAL_VALUE = 26, IDENTIFIER = 27, WS = 28; public static final String[] ruleNames = makeRuleNames(); @@ -49,7 +49,7 @@ public class FiltersLexer extends Lexer { @Deprecated public static final String[] tokenNames; - public static final String _serializedATN = "\u0004\u0000\u001a\u00e6\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002" + public static final String _serializedATN = "\u0004\u0000\u001c\u00fa\u0006\uffff\uffff\u0002\u0000\u0007\u0000\u0002" + "\u0001\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002" + "\u0004\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002" + "\u0007\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002" @@ -58,148 +58,162 @@ public class FiltersLexer extends Lexer { + "\u0002\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014" + "\u0002\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017" + "\u0002\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a" - + "\u0002\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0001\u0000\u0001\u0000" + + "\u0002\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d" + + "\u0002\u001e\u0007\u001e\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000" + "\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000" - + "\u0001\u0000\u0001\u0000\u0003\u0000F\b\u0000\u0001\u0001\u0001\u0001" - + "\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004" - + "\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007" - + "\u0001\u0007\u0001\b\u0001\b\u0001\t\u0001\t\u0001\n\u0001\n\u0001\u000b" - + "\u0001\u000b\u0001\u000b\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001" - + "\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001\u000f\u0001\u000f\u0001" - + "\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0003\u000fp\b" - + "\u000f\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001" - + "\u0010\u0003\u0010x\b\u0010\u0001\u0011\u0001\u0011\u0001\u0011\u0001" - + "\u0011\u0003\u0011~\b\u0011\u0001\u0012\u0001\u0012\u0001\u0012\u0001" - + "\u0012\u0001\u0012\u0001\u0012\u0003\u0012\u0086\b\u0012\u0001\u0013\u0001" - + "\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0003\u0013\u008e" - + "\b\u0013\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001" - + "\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001" - + "\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001" - + "\u0014\u0003\u0014\u00a2\b\u0014\u0001\u0015\u0001\u0015\u0001\u0015\u0001" - + "\u0015\u0005\u0015\u00a8\b\u0015\n\u0015\f\u0015\u00ab\t\u0015\u0001\u0015" - + "\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0005\u0015\u00b2\b\u0015" - + "\n\u0015\f\u0015\u00b5\t\u0015\u0001\u0015\u0003\u0015\u00b8\b\u0015\u0001" - + "\u0016\u0004\u0016\u00bb\b\u0016\u000b\u0016\f\u0016\u00bc\u0001\u0017" - + "\u0001\u0017\u0001\u0018\u0001\u0018\u0001\u0018\u0004\u0018\u00c4\b\u0018" - + "\u000b\u0018\f\u0018\u00c5\u0001\u0019\u0004\u0019\u00c9\b\u0019\u000b" - + "\u0019\f\u0019\u00ca\u0001\u0019\u0001\u0019\u0005\u0019\u00cf\b\u0019" - + "\n\u0019\f\u0019\u00d2\t\u0019\u0001\u0019\u0001\u0019\u0004\u0019\u00d6" - + "\b\u0019\u000b\u0019\f\u0019\u00d7\u0003\u0019\u00da\b\u0019\u0001\u001a" - + "\u0001\u001a\u0001\u001b\u0001\u001b\u0001\u001c\u0004\u001c\u00e1\b\u001c" - + "\u000b\u001c\f\u001c\u00e2\u0001\u001c\u0001\u001c\u0000\u0000\u001d\u0001" - + "\u0001\u0003\u0002\u0005\u0003\u0007\u0004\t\u0005\u000b\u0006\r\u0007" - + "\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017\f\u0019\r\u001b\u000e\u001d" - + "\u000f\u001f\u0010!\u0011#\u0012%\u0013\'\u0014)\u0015+\u0016-\u0017/" - + "\u00181\u00193\u00005\u00007\u00009\u001a\u0001\u0000\u0005\u0002\u0000" - + "\'\'\\\\\u0002\u0000\"\"\\\\\u0001\u000009\u0002\u0000AZaz\u0003\u0000" - + "\t\n\r\r \u00fb\u0000\u0001\u0001\u0000\u0000\u0000\u0000\u0003\u0001" - + "\u0000\u0000\u0000\u0000\u0005\u0001\u0000\u0000\u0000\u0000\u0007\u0001" - + "\u0000\u0000\u0000\u0000\t\u0001\u0000\u0000\u0000\u0000\u000b\u0001\u0000" - + "\u0000\u0000\u0000\r\u0001\u0000\u0000\u0000\u0000\u000f\u0001\u0000\u0000" - + "\u0000\u0000\u0011\u0001\u0000\u0000\u0000\u0000\u0013\u0001\u0000\u0000" - + "\u0000\u0000\u0015\u0001\u0000\u0000\u0000\u0000\u0017\u0001\u0000\u0000" - + "\u0000\u0000\u0019\u0001\u0000\u0000\u0000\u0000\u001b\u0001\u0000\u0000" - + "\u0000\u0000\u001d\u0001\u0000\u0000\u0000\u0000\u001f\u0001\u0000\u0000" - + "\u0000\u0000!\u0001\u0000\u0000\u0000\u0000#\u0001\u0000\u0000\u0000\u0000" - + "%\u0001\u0000\u0000\u0000\u0000\'\u0001\u0000\u0000\u0000\u0000)\u0001" - + "\u0000\u0000\u0000\u0000+\u0001\u0000\u0000\u0000\u0000-\u0001\u0000\u0000" - + "\u0000\u0000/\u0001\u0000\u0000\u0000\u00001\u0001\u0000\u0000\u0000\u0000" - + "9\u0001\u0000\u0000\u0000\u0001E\u0001\u0000\u0000\u0000\u0003G\u0001" - + "\u0000\u0000\u0000\u0005I\u0001\u0000\u0000\u0000\u0007K\u0001\u0000\u0000" - + "\u0000\tM\u0001\u0000\u0000\u0000\u000bO\u0001\u0000\u0000\u0000\rQ\u0001" - + "\u0000\u0000\u0000\u000fS\u0001\u0000\u0000\u0000\u0011V\u0001\u0000\u0000" - + "\u0000\u0013X\u0001\u0000\u0000\u0000\u0015Z\u0001\u0000\u0000\u0000\u0017" - + "\\\u0001\u0000\u0000\u0000\u0019_\u0001\u0000\u0000\u0000\u001ba\u0001" - + "\u0000\u0000\u0000\u001dd\u0001\u0000\u0000\u0000\u001fo\u0001\u0000\u0000" - + "\u0000!w\u0001\u0000\u0000\u0000#}\u0001\u0000\u0000\u0000%\u0085\u0001" - + "\u0000\u0000\u0000\'\u008d\u0001\u0000\u0000\u0000)\u00a1\u0001\u0000" - + "\u0000\u0000+\u00b7\u0001\u0000\u0000\u0000-\u00ba\u0001\u0000\u0000\u0000" - + "/\u00be\u0001\u0000\u0000\u00001\u00c3\u0001\u0000\u0000\u00003\u00d9" - + "\u0001\u0000\u0000\u00005\u00db\u0001\u0000\u0000\u00007\u00dd\u0001\u0000" - + "\u0000\u00009\u00e0\u0001\u0000\u0000\u0000;<\u0005W\u0000\u0000<=\u0005" - + "H\u0000\u0000=>\u0005E\u0000\u0000>?\u0005R\u0000\u0000?F\u0005E\u0000" - + "\u0000@A\u0005w\u0000\u0000AB\u0005h\u0000\u0000BC\u0005e\u0000\u0000" - + "CD\u0005r\u0000\u0000DF\u0005e\u0000\u0000E;\u0001\u0000\u0000\u0000E" - + "@\u0001\u0000\u0000\u0000F\u0002\u0001\u0000\u0000\u0000GH\u0005.\u0000" - + "\u0000H\u0004\u0001\u0000\u0000\u0000IJ\u0005,\u0000\u0000J\u0006\u0001" - + "\u0000\u0000\u0000KL\u0005[\u0000\u0000L\b\u0001\u0000\u0000\u0000MN\u0005" - + "]\u0000\u0000N\n\u0001\u0000\u0000\u0000OP\u0005(\u0000\u0000P\f\u0001" - + "\u0000\u0000\u0000QR\u0005)\u0000\u0000R\u000e\u0001\u0000\u0000\u0000" - + "ST\u0005=\u0000\u0000TU\u0005=\u0000\u0000U\u0010\u0001\u0000\u0000\u0000" - + "VW\u0005-\u0000\u0000W\u0012\u0001\u0000\u0000\u0000XY\u0005+\u0000\u0000" - + "Y\u0014\u0001\u0000\u0000\u0000Z[\u0005>\u0000\u0000[\u0016\u0001\u0000" - + "\u0000\u0000\\]\u0005>\u0000\u0000]^\u0005=\u0000\u0000^\u0018\u0001\u0000" - + "\u0000\u0000_`\u0005<\u0000\u0000`\u001a\u0001\u0000\u0000\u0000ab\u0005" - + "<\u0000\u0000bc\u0005=\u0000\u0000c\u001c\u0001\u0000\u0000\u0000de\u0005" - + "!\u0000\u0000ef\u0005=\u0000\u0000f\u001e\u0001\u0000\u0000\u0000gh\u0005" - + "A\u0000\u0000hi\u0005N\u0000\u0000ip\u0005D\u0000\u0000jk\u0005a\u0000" - + "\u0000kl\u0005n\u0000\u0000lp\u0005d\u0000\u0000mn\u0005&\u0000\u0000" - + "np\u0005&\u0000\u0000og\u0001\u0000\u0000\u0000oj\u0001\u0000\u0000\u0000" - + "om\u0001\u0000\u0000\u0000p \u0001\u0000\u0000\u0000qr\u0005O\u0000\u0000" - + "rx\u0005R\u0000\u0000st\u0005o\u0000\u0000tx\u0005r\u0000\u0000uv\u0005" - + "|\u0000\u0000vx\u0005|\u0000\u0000wq\u0001\u0000\u0000\u0000ws\u0001\u0000" - + "\u0000\u0000wu\u0001\u0000\u0000\u0000x\"\u0001\u0000\u0000\u0000yz\u0005" - + "I\u0000\u0000z~\u0005N\u0000\u0000{|\u0005i\u0000\u0000|~\u0005n\u0000" - + "\u0000}y\u0001\u0000\u0000\u0000}{\u0001\u0000\u0000\u0000~$\u0001\u0000" - + "\u0000\u0000\u007f\u0080\u0005N\u0000\u0000\u0080\u0081\u0005I\u0000\u0000" - + "\u0081\u0086\u0005N\u0000\u0000\u0082\u0083\u0005n\u0000\u0000\u0083\u0084" - + "\u0005i\u0000\u0000\u0084\u0086\u0005n\u0000\u0000\u0085\u007f\u0001\u0000" - + "\u0000\u0000\u0085\u0082\u0001\u0000\u0000\u0000\u0086&\u0001\u0000\u0000" - + "\u0000\u0087\u0088\u0005N\u0000\u0000\u0088\u0089\u0005O\u0000\u0000\u0089" - + "\u008e\u0005T\u0000\u0000\u008a\u008b\u0005n\u0000\u0000\u008b\u008c\u0005" - + "o\u0000\u0000\u008c\u008e\u0005t\u0000\u0000\u008d\u0087\u0001\u0000\u0000" - + "\u0000\u008d\u008a\u0001\u0000\u0000\u0000\u008e(\u0001\u0000\u0000\u0000" - + "\u008f\u0090\u0005T\u0000\u0000\u0090\u0091\u0005R\u0000\u0000\u0091\u0092" - + "\u0005U\u0000\u0000\u0092\u00a2\u0005E\u0000\u0000\u0093\u0094\u0005t" - + "\u0000\u0000\u0094\u0095\u0005r\u0000\u0000\u0095\u0096\u0005u\u0000\u0000" - + "\u0096\u00a2\u0005e\u0000\u0000\u0097\u0098\u0005F\u0000\u0000\u0098\u0099" - + "\u0005A\u0000\u0000\u0099\u009a\u0005L\u0000\u0000\u009a\u009b\u0005S" - + "\u0000\u0000\u009b\u00a2\u0005E\u0000\u0000\u009c\u009d\u0005f\u0000\u0000" - + "\u009d\u009e\u0005a\u0000\u0000\u009e\u009f\u0005l\u0000\u0000\u009f\u00a0" - + "\u0005s\u0000\u0000\u00a0\u00a2\u0005e\u0000\u0000\u00a1\u008f\u0001\u0000" - + "\u0000\u0000\u00a1\u0093\u0001\u0000\u0000\u0000\u00a1\u0097\u0001\u0000" - + "\u0000\u0000\u00a1\u009c\u0001\u0000\u0000\u0000\u00a2*\u0001\u0000\u0000" - + "\u0000\u00a3\u00a9\u0005\'\u0000\u0000\u00a4\u00a8\b\u0000\u0000\u0000" - + "\u00a5\u00a6\u0005\\\u0000\u0000\u00a6\u00a8\t\u0000\u0000\u0000\u00a7" - + "\u00a4\u0001\u0000\u0000\u0000\u00a7\u00a5\u0001\u0000\u0000\u0000\u00a8" - + "\u00ab\u0001\u0000\u0000\u0000\u00a9\u00a7\u0001\u0000\u0000\u0000\u00a9" - + "\u00aa\u0001\u0000\u0000\u0000\u00aa\u00ac\u0001\u0000\u0000\u0000\u00ab" - + "\u00a9\u0001\u0000\u0000\u0000\u00ac\u00b8\u0005\'\u0000\u0000\u00ad\u00b3" - + "\u0005\"\u0000\u0000\u00ae\u00b2\b\u0001\u0000\u0000\u00af\u00b0\u0005" - + "\\\u0000\u0000\u00b0\u00b2\t\u0000\u0000\u0000\u00b1\u00ae\u0001\u0000" - + "\u0000\u0000\u00b1\u00af\u0001\u0000\u0000\u0000\u00b2\u00b5\u0001\u0000" - + "\u0000\u0000\u00b3\u00b1\u0001\u0000\u0000\u0000\u00b3\u00b4\u0001\u0000" - + "\u0000\u0000\u00b4\u00b6\u0001\u0000\u0000\u0000\u00b5\u00b3\u0001\u0000" - + "\u0000\u0000\u00b6\u00b8\u0005\"\u0000\u0000\u00b7\u00a3\u0001\u0000\u0000" - + "\u0000\u00b7\u00ad\u0001\u0000\u0000\u0000\u00b8,\u0001\u0000\u0000\u0000" - + "\u00b9\u00bb\u00035\u001a\u0000\u00ba\u00b9\u0001\u0000\u0000\u0000\u00bb" - + "\u00bc\u0001\u0000\u0000\u0000\u00bc\u00ba\u0001\u0000\u0000\u0000\u00bc" - + "\u00bd\u0001\u0000\u0000\u0000\u00bd.\u0001\u0000\u0000\u0000\u00be\u00bf" - + "\u00033\u0019\u0000\u00bf0\u0001\u0000\u0000\u0000\u00c0\u00c4\u00037" - + "\u001b\u0000\u00c1\u00c4\u00035\u001a\u0000\u00c2\u00c4\u0005_\u0000\u0000" - + "\u00c3\u00c0\u0001\u0000\u0000\u0000\u00c3\u00c1\u0001\u0000\u0000\u0000" - + "\u00c3\u00c2\u0001\u0000\u0000\u0000\u00c4\u00c5\u0001\u0000\u0000\u0000" - + "\u00c5\u00c3\u0001\u0000\u0000\u0000\u00c5\u00c6\u0001\u0000\u0000\u0000" - + "\u00c62\u0001\u0000\u0000\u0000\u00c7\u00c9\u00035\u001a\u0000\u00c8\u00c7" - + "\u0001\u0000\u0000\u0000\u00c9\u00ca\u0001\u0000\u0000\u0000\u00ca\u00c8" - + "\u0001\u0000\u0000\u0000\u00ca\u00cb\u0001\u0000\u0000\u0000\u00cb\u00cc" - + "\u0001\u0000\u0000\u0000\u00cc\u00d0\u0005.\u0000\u0000\u00cd\u00cf\u0003" - + "5\u001a\u0000\u00ce\u00cd\u0001\u0000\u0000\u0000\u00cf\u00d2\u0001\u0000" - + "\u0000\u0000\u00d0\u00ce\u0001\u0000\u0000\u0000\u00d0\u00d1\u0001\u0000" - + "\u0000\u0000\u00d1\u00da\u0001\u0000\u0000\u0000\u00d2\u00d0\u0001\u0000" - + "\u0000\u0000\u00d3\u00d5\u0005.\u0000\u0000\u00d4\u00d6\u00035\u001a\u0000" - + "\u00d5\u00d4\u0001\u0000\u0000\u0000\u00d6\u00d7\u0001\u0000\u0000\u0000" - + "\u00d7\u00d5\u0001\u0000\u0000\u0000\u00d7\u00d8\u0001\u0000\u0000\u0000" - + "\u00d8\u00da\u0001\u0000\u0000\u0000\u00d9\u00c8\u0001\u0000\u0000\u0000" - + "\u00d9\u00d3\u0001\u0000\u0000\u0000\u00da4\u0001\u0000\u0000\u0000\u00db" - + "\u00dc\u0007\u0002\u0000\u0000\u00dc6\u0001\u0000\u0000\u0000\u00dd\u00de" - + "\u0007\u0003\u0000\u0000\u00de8\u0001\u0000\u0000\u0000\u00df\u00e1\u0007" - + "\u0004\u0000\u0000\u00e0\u00df\u0001\u0000\u0000\u0000\u00e1\u00e2\u0001" - + "\u0000\u0000\u0000\u00e2\u00e0\u0001\u0000\u0000\u0000\u00e2\u00e3\u0001" - + "\u0000\u0000\u0000\u00e3\u00e4\u0001\u0000\u0000\u0000\u00e4\u00e5\u0006" - + "\u001c\u0000\u0000\u00e5:\u0001\u0000\u0000\u0000\u0015\u0000Eow}\u0085" - + "\u008d\u00a1\u00a7\u00a9\u00b1\u00b3\u00b7\u00bc\u00c3\u00c5\u00ca\u00d0" - + "\u00d7\u00d9\u00e2\u0001\u0000\u0001\u0000"; + + "\u0003\u0000J\b\u0000\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002" + + "\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005" + + "\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0001\b\u0001" + + "\b\u0001\t\u0001\t\u0001\n\u0001\n\u0001\u000b\u0001\u000b\u0001\u000b" + + "\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001\u000e\u0001\u000e\u0001" + + "\u000e\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001" + + "\u000f\u0001\u000f\u0001\u000f\u0003\u000ft\b\u000f\u0001\u0010\u0001" + + "\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0003\u0010|\b" + + "\u0010\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0003\u0011\u0082" + + "\b\u0011\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001" + + "\u0012\u0003\u0012\u008a\b\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001" + + "\u0013\u0001\u0013\u0001\u0013\u0003\u0013\u0092\b\u0013\u0001\u0014\u0001" + + "\u0014\u0001\u0014\u0001\u0014\u0003\u0014\u0098\b\u0014\u0001\u0015\u0001" + + "\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001\u0015\u0001" + + "\u0015\u0003\u0015\u00a2\b\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001" + + "\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001" + + "\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001" + + "\u0016\u0001\u0016\u0001\u0016\u0003\u0016\u00b6\b\u0016\u0001\u0017\u0001" + + "\u0017\u0001\u0017\u0001\u0017\u0005\u0017\u00bc\b\u0017\n\u0017\f\u0017" + + "\u00bf\t\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017" + + "\u0005\u0017\u00c6\b\u0017\n\u0017\f\u0017\u00c9\t\u0017\u0001\u0017\u0003" + + "\u0017\u00cc\b\u0017\u0001\u0018\u0004\u0018\u00cf\b\u0018\u000b\u0018" + + "\f\u0018\u00d0\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001\u001a" + + "\u0004\u001a\u00d8\b\u001a\u000b\u001a\f\u001a\u00d9\u0001\u001b\u0004" + + "\u001b\u00dd\b\u001b\u000b\u001b\f\u001b\u00de\u0001\u001b\u0001\u001b" + + "\u0005\u001b\u00e3\b\u001b\n\u001b\f\u001b\u00e6\t\u001b\u0001\u001b\u0001" + + "\u001b\u0004\u001b\u00ea\b\u001b\u000b\u001b\f\u001b\u00eb\u0003\u001b" + + "\u00ee\b\u001b\u0001\u001c\u0001\u001c\u0001\u001d\u0001\u001d\u0001\u001e" + + "\u0004\u001e\u00f5\b\u001e\u000b\u001e\f\u001e\u00f6\u0001\u001e\u0001" + + "\u001e\u0000\u0000\u001f\u0001\u0001\u0003\u0002\u0005\u0003\u0007\u0004" + + "\t\u0005\u000b\u0006\r\u0007\u000f\b\u0011\t\u0013\n\u0015\u000b\u0017" + + "\f\u0019\r\u001b\u000e\u001d\u000f\u001f\u0010!\u0011#\u0012%\u0013\'" + + "\u0014)\u0015+\u0016-\u0017/\u00181\u00193\u001a5\u001b7\u00009\u0000" + + ";\u0000=\u001c\u0001\u0000\u0005\u0002\u0000\'\'\\\\\u0002\u0000\"\"\\" + + "\\\u0001\u000009\u0002\u0000AZaz\u0003\u0000\t\n\r\r \u0111\u0000\u0001" + + "\u0001\u0000\u0000\u0000\u0000\u0003\u0001\u0000\u0000\u0000\u0000\u0005" + + "\u0001\u0000\u0000\u0000\u0000\u0007\u0001\u0000\u0000\u0000\u0000\t\u0001" + + "\u0000\u0000\u0000\u0000\u000b\u0001\u0000\u0000\u0000\u0000\r\u0001\u0000" + + "\u0000\u0000\u0000\u000f\u0001\u0000\u0000\u0000\u0000\u0011\u0001\u0000" + + "\u0000\u0000\u0000\u0013\u0001\u0000\u0000\u0000\u0000\u0015\u0001\u0000" + + "\u0000\u0000\u0000\u0017\u0001\u0000\u0000\u0000\u0000\u0019\u0001\u0000" + + "\u0000\u0000\u0000\u001b\u0001\u0000\u0000\u0000\u0000\u001d\u0001\u0000" + + "\u0000\u0000\u0000\u001f\u0001\u0000\u0000\u0000\u0000!\u0001\u0000\u0000" + + "\u0000\u0000#\u0001\u0000\u0000\u0000\u0000%\u0001\u0000\u0000\u0000\u0000" + + "\'\u0001\u0000\u0000\u0000\u0000)\u0001\u0000\u0000\u0000\u0000+\u0001" + + "\u0000\u0000\u0000\u0000-\u0001\u0000\u0000\u0000\u0000/\u0001\u0000\u0000" + + "\u0000\u00001\u0001\u0000\u0000\u0000\u00003\u0001\u0000\u0000\u0000\u0000" + + "5\u0001\u0000\u0000\u0000\u0000=\u0001\u0000\u0000\u0000\u0001I\u0001" + + "\u0000\u0000\u0000\u0003K\u0001\u0000\u0000\u0000\u0005M\u0001\u0000\u0000" + + "\u0000\u0007O\u0001\u0000\u0000\u0000\tQ\u0001\u0000\u0000\u0000\u000b" + + "S\u0001\u0000\u0000\u0000\rU\u0001\u0000\u0000\u0000\u000fW\u0001\u0000" + + "\u0000\u0000\u0011Z\u0001\u0000\u0000\u0000\u0013\\\u0001\u0000\u0000" + + "\u0000\u0015^\u0001\u0000\u0000\u0000\u0017`\u0001\u0000\u0000\u0000\u0019" + + "c\u0001\u0000\u0000\u0000\u001be\u0001\u0000\u0000\u0000\u001dh\u0001" + + "\u0000\u0000\u0000\u001fs\u0001\u0000\u0000\u0000!{\u0001\u0000\u0000" + + "\u0000#\u0081\u0001\u0000\u0000\u0000%\u0089\u0001\u0000\u0000\u0000\'" + + "\u0091\u0001\u0000\u0000\u0000)\u0097\u0001\u0000\u0000\u0000+\u00a1\u0001" + + "\u0000\u0000\u0000-\u00b5\u0001\u0000\u0000\u0000/\u00cb\u0001\u0000\u0000" + + "\u00001\u00ce\u0001\u0000\u0000\u00003\u00d2\u0001\u0000\u0000\u00005" + + "\u00d7\u0001\u0000\u0000\u00007\u00ed\u0001\u0000\u0000\u00009\u00ef\u0001" + + "\u0000\u0000\u0000;\u00f1\u0001\u0000\u0000\u0000=\u00f4\u0001\u0000\u0000" + + "\u0000?@\u0005W\u0000\u0000@A\u0005H\u0000\u0000AB\u0005E\u0000\u0000" + + "BC\u0005R\u0000\u0000CJ\u0005E\u0000\u0000DE\u0005w\u0000\u0000EF\u0005" + + "h\u0000\u0000FG\u0005e\u0000\u0000GH\u0005r\u0000\u0000HJ\u0005e\u0000" + + "\u0000I?\u0001\u0000\u0000\u0000ID\u0001\u0000\u0000\u0000J\u0002\u0001" + + "\u0000\u0000\u0000KL\u0005.\u0000\u0000L\u0004\u0001\u0000\u0000\u0000" + + "MN\u0005,\u0000\u0000N\u0006\u0001\u0000\u0000\u0000OP\u0005[\u0000\u0000" + + "P\b\u0001\u0000\u0000\u0000QR\u0005]\u0000\u0000R\n\u0001\u0000\u0000" + + "\u0000ST\u0005(\u0000\u0000T\f\u0001\u0000\u0000\u0000UV\u0005)\u0000" + + "\u0000V\u000e\u0001\u0000\u0000\u0000WX\u0005=\u0000\u0000XY\u0005=\u0000" + + "\u0000Y\u0010\u0001\u0000\u0000\u0000Z[\u0005-\u0000\u0000[\u0012\u0001" + + "\u0000\u0000\u0000\\]\u0005+\u0000\u0000]\u0014\u0001\u0000\u0000\u0000" + + "^_\u0005>\u0000\u0000_\u0016\u0001\u0000\u0000\u0000`a\u0005>\u0000\u0000" + + "ab\u0005=\u0000\u0000b\u0018\u0001\u0000\u0000\u0000cd\u0005<\u0000\u0000" + + "d\u001a\u0001\u0000\u0000\u0000ef\u0005<\u0000\u0000fg\u0005=\u0000\u0000" + + "g\u001c\u0001\u0000\u0000\u0000hi\u0005!\u0000\u0000ij\u0005=\u0000\u0000" + + "j\u001e\u0001\u0000\u0000\u0000kl\u0005A\u0000\u0000lm\u0005N\u0000\u0000" + + "mt\u0005D\u0000\u0000no\u0005a\u0000\u0000op\u0005n\u0000\u0000pt\u0005" + + "d\u0000\u0000qr\u0005&\u0000\u0000rt\u0005&\u0000\u0000sk\u0001\u0000" + + "\u0000\u0000sn\u0001\u0000\u0000\u0000sq\u0001\u0000\u0000\u0000t \u0001" + + "\u0000\u0000\u0000uv\u0005O\u0000\u0000v|\u0005R\u0000\u0000wx\u0005o" + + "\u0000\u0000x|\u0005r\u0000\u0000yz\u0005|\u0000\u0000z|\u0005|\u0000" + + "\u0000{u\u0001\u0000\u0000\u0000{w\u0001\u0000\u0000\u0000{y\u0001\u0000" + + "\u0000\u0000|\"\u0001\u0000\u0000\u0000}~\u0005I\u0000\u0000~\u0082\u0005" + + "N\u0000\u0000\u007f\u0080\u0005i\u0000\u0000\u0080\u0082\u0005n\u0000" + + "\u0000\u0081}\u0001\u0000\u0000\u0000\u0081\u007f\u0001\u0000\u0000\u0000" + + "\u0082$\u0001\u0000\u0000\u0000\u0083\u0084\u0005N\u0000\u0000\u0084\u0085" + + "\u0005I\u0000\u0000\u0085\u008a\u0005N\u0000\u0000\u0086\u0087\u0005n" + + "\u0000\u0000\u0087\u0088\u0005i\u0000\u0000\u0088\u008a\u0005n\u0000\u0000" + + "\u0089\u0083\u0001\u0000\u0000\u0000\u0089\u0086\u0001\u0000\u0000\u0000" + + "\u008a&\u0001\u0000\u0000\u0000\u008b\u008c\u0005N\u0000\u0000\u008c\u008d" + + "\u0005O\u0000\u0000\u008d\u0092\u0005T\u0000\u0000\u008e\u008f\u0005n" + + "\u0000\u0000\u008f\u0090\u0005o\u0000\u0000\u0090\u0092\u0005t\u0000\u0000" + + "\u0091\u008b\u0001\u0000\u0000\u0000\u0091\u008e\u0001\u0000\u0000\u0000" + + "\u0092(\u0001\u0000\u0000\u0000\u0093\u0094\u0005I\u0000\u0000\u0094\u0098" + + "\u0005S\u0000\u0000\u0095\u0096\u0005i\u0000\u0000\u0096\u0098\u0005s" + + "\u0000\u0000\u0097\u0093\u0001\u0000\u0000\u0000\u0097\u0095\u0001\u0000" + + "\u0000\u0000\u0098*\u0001\u0000\u0000\u0000\u0099\u009a\u0005N\u0000\u0000" + + "\u009a\u009b\u0005U\u0000\u0000\u009b\u009c\u0005L\u0000\u0000\u009c\u00a2" + + "\u0005L\u0000\u0000\u009d\u009e\u0005n\u0000\u0000\u009e\u009f\u0005u" + + "\u0000\u0000\u009f\u00a0\u0005l\u0000\u0000\u00a0\u00a2\u0005l\u0000\u0000" + + "\u00a1\u0099\u0001\u0000\u0000\u0000\u00a1\u009d\u0001\u0000\u0000\u0000" + + "\u00a2,\u0001\u0000\u0000\u0000\u00a3\u00a4\u0005T\u0000\u0000\u00a4\u00a5" + + "\u0005R\u0000\u0000\u00a5\u00a6\u0005U\u0000\u0000\u00a6\u00b6\u0005E" + + "\u0000\u0000\u00a7\u00a8\u0005t\u0000\u0000\u00a8\u00a9\u0005r\u0000\u0000" + + "\u00a9\u00aa\u0005u\u0000\u0000\u00aa\u00b6\u0005e\u0000\u0000\u00ab\u00ac" + + "\u0005F\u0000\u0000\u00ac\u00ad\u0005A\u0000\u0000\u00ad\u00ae\u0005L" + + "\u0000\u0000\u00ae\u00af\u0005S\u0000\u0000\u00af\u00b6\u0005E\u0000\u0000" + + "\u00b0\u00b1\u0005f\u0000\u0000\u00b1\u00b2\u0005a\u0000\u0000\u00b2\u00b3" + + "\u0005l\u0000\u0000\u00b3\u00b4\u0005s\u0000\u0000\u00b4\u00b6\u0005e" + + "\u0000\u0000\u00b5\u00a3\u0001\u0000\u0000\u0000\u00b5\u00a7\u0001\u0000" + + "\u0000\u0000\u00b5\u00ab\u0001\u0000\u0000\u0000\u00b5\u00b0\u0001\u0000" + + "\u0000\u0000\u00b6.\u0001\u0000\u0000\u0000\u00b7\u00bd\u0005\'\u0000" + + "\u0000\u00b8\u00bc\b\u0000\u0000\u0000\u00b9\u00ba\u0005\\\u0000\u0000" + + "\u00ba\u00bc\t\u0000\u0000\u0000\u00bb\u00b8\u0001\u0000\u0000\u0000\u00bb" + + "\u00b9\u0001\u0000\u0000\u0000\u00bc\u00bf\u0001\u0000\u0000\u0000\u00bd" + + "\u00bb\u0001\u0000\u0000\u0000\u00bd\u00be\u0001\u0000\u0000\u0000\u00be" + + "\u00c0\u0001\u0000\u0000\u0000\u00bf\u00bd\u0001\u0000\u0000\u0000\u00c0" + + "\u00cc\u0005\'\u0000\u0000\u00c1\u00c7\u0005\"\u0000\u0000\u00c2\u00c6" + + "\b\u0001\u0000\u0000\u00c3\u00c4\u0005\\\u0000\u0000\u00c4\u00c6\t\u0000" + + "\u0000\u0000\u00c5\u00c2\u0001\u0000\u0000\u0000\u00c5\u00c3\u0001\u0000" + + "\u0000\u0000\u00c6\u00c9\u0001\u0000\u0000\u0000\u00c7\u00c5\u0001\u0000" + + "\u0000\u0000\u00c7\u00c8\u0001\u0000\u0000\u0000\u00c8\u00ca\u0001\u0000" + + "\u0000\u0000\u00c9\u00c7\u0001\u0000\u0000\u0000\u00ca\u00cc\u0005\"\u0000" + + "\u0000\u00cb\u00b7\u0001\u0000\u0000\u0000\u00cb\u00c1\u0001\u0000\u0000" + + "\u0000\u00cc0\u0001\u0000\u0000\u0000\u00cd\u00cf\u00039\u001c\u0000\u00ce" + + "\u00cd\u0001\u0000\u0000\u0000\u00cf\u00d0\u0001\u0000\u0000\u0000\u00d0" + + "\u00ce\u0001\u0000\u0000\u0000\u00d0\u00d1\u0001\u0000\u0000\u0000\u00d1" + + "2\u0001\u0000\u0000\u0000\u00d2\u00d3\u00037\u001b\u0000\u00d34\u0001" + + "\u0000\u0000\u0000\u00d4\u00d8\u0003;\u001d\u0000\u00d5\u00d8\u00039\u001c" + + "\u0000\u00d6\u00d8\u0005_\u0000\u0000\u00d7\u00d4\u0001\u0000\u0000\u0000" + + "\u00d7\u00d5\u0001\u0000\u0000\u0000\u00d7\u00d6\u0001\u0000\u0000\u0000" + + "\u00d8\u00d9\u0001\u0000\u0000\u0000\u00d9\u00d7\u0001\u0000\u0000\u0000" + + "\u00d9\u00da\u0001\u0000\u0000\u0000\u00da6\u0001\u0000\u0000\u0000\u00db" + + "\u00dd\u00039\u001c\u0000\u00dc\u00db\u0001\u0000\u0000\u0000\u00dd\u00de" + + "\u0001\u0000\u0000\u0000\u00de\u00dc\u0001\u0000\u0000\u0000\u00de\u00df" + + "\u0001\u0000\u0000\u0000\u00df\u00e0\u0001\u0000\u0000\u0000\u00e0\u00e4" + + "\u0005.\u0000\u0000\u00e1\u00e3\u00039\u001c\u0000\u00e2\u00e1\u0001\u0000" + + "\u0000\u0000\u00e3\u00e6\u0001\u0000\u0000\u0000\u00e4\u00e2\u0001\u0000" + + "\u0000\u0000\u00e4\u00e5\u0001\u0000\u0000\u0000\u00e5\u00ee\u0001\u0000" + + "\u0000\u0000\u00e6\u00e4\u0001\u0000\u0000\u0000\u00e7\u00e9\u0005.\u0000" + + "\u0000\u00e8\u00ea\u00039\u001c\u0000\u00e9\u00e8\u0001\u0000\u0000\u0000" + + "\u00ea\u00eb\u0001\u0000\u0000\u0000\u00eb\u00e9\u0001\u0000\u0000\u0000" + + "\u00eb\u00ec\u0001\u0000\u0000\u0000\u00ec\u00ee\u0001\u0000\u0000\u0000" + + "\u00ed\u00dc\u0001\u0000\u0000\u0000\u00ed\u00e7\u0001\u0000\u0000\u0000" + + "\u00ee8\u0001\u0000\u0000\u0000\u00ef\u00f0\u0007\u0002\u0000\u0000\u00f0" + + ":\u0001\u0000\u0000\u0000\u00f1\u00f2\u0007\u0003\u0000\u0000\u00f2<\u0001" + + "\u0000\u0000\u0000\u00f3\u00f5\u0007\u0004\u0000\u0000\u00f4\u00f3\u0001" + + "\u0000\u0000\u0000\u00f5\u00f6\u0001\u0000\u0000\u0000\u00f6\u00f4\u0001" + + "\u0000\u0000\u0000\u00f6\u00f7\u0001\u0000\u0000\u0000\u00f7\u00f8\u0001" + + "\u0000\u0000\u0000\u00f8\u00f9\u0006\u001e\u0000\u0000\u00f9>\u0001\u0000" + + "\u0000\u0000\u0017\u0000Is{\u0081\u0089\u0091\u0097\u00a1\u00b5\u00bb" + + "\u00bd\u00c5\u00c7\u00cb\u00d0\u00d7\u00d9\u00de\u00e4\u00eb\u00ed\u00f6\u0001\u0000\u0001\u0000"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); @@ -225,8 +239,8 @@ public FiltersLexer(CharStream input) { private static String[] makeRuleNames() { return new String[] { "WHERE", "DOT", "COMMA", "LEFT_SQUARE_BRACKETS", "RIGHT_SQUARE_BRACKETS", "LEFT_PARENTHESIS", "RIGHT_PARENTHESIS", "EQUALS", "MINUS", "PLUS", "GT", "GE", "LT", "LE", "NE", "AND", - "OR", "IN", "NIN", "NOT", "BOOLEAN_VALUE", "QUOTED_STRING", "INTEGER_VALUE", "DECIMAL_VALUE", - "IDENTIFIER", "DECIMAL_DIGITS", "DIGIT", "LETTER", "WS" }; + "OR", "IN", "NIN", "NOT", "IS", "NULL", "BOOLEAN_VALUE", "QUOTED_STRING", "INTEGER_VALUE", + "DECIMAL_VALUE", "IDENTIFIER", "DECIMAL_DIGITS", "DIGIT", "LETTER", "WS" }; } private static String[] makeLiteralNames() { @@ -237,8 +251,8 @@ private static String[] makeLiteralNames() { private static String[] makeSymbolicNames() { return new String[] { null, "WHERE", "DOT", "COMMA", "LEFT_SQUARE_BRACKETS", "RIGHT_SQUARE_BRACKETS", "LEFT_PARENTHESIS", "RIGHT_PARENTHESIS", "EQUALS", "MINUS", "PLUS", "GT", "GE", "LT", "LE", "NE", "AND", - "OR", "IN", "NIN", "NOT", "BOOLEAN_VALUE", "QUOTED_STRING", "INTEGER_VALUE", "DECIMAL_VALUE", - "IDENTIFIER", "WS" }; + "OR", "IN", "NIN", "NOT", "IS", "NULL", "BOOLEAN_VALUE", "QUOTED_STRING", "INTEGER_VALUE", + "DECIMAL_VALUE", "IDENTIFIER", "WS" }; } @Override diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersListener.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersListener.java index 8e49aeff6b3..9f2a9bdced4 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersListener.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,6 +56,34 @@ public interface FiltersListener extends ParseTreeListener { */ void exitNinExpression(FiltersParser.NinExpressionContext ctx); + /** + * Enter a parse tree produced by the {@code IsNullExpression} labeled alternative in + * {@link FiltersParser#booleanExpression}. + * @param ctx the parse tree + */ + void enterIsNullExpression(FiltersParser.IsNullExpressionContext ctx); + + /** + * Exit a parse tree produced by the {@code IsNullExpression} labeled alternative in + * {@link FiltersParser#booleanExpression}. + * @param ctx the parse tree + */ + void exitIsNullExpression(FiltersParser.IsNullExpressionContext ctx); + + /** + * Enter a parse tree produced by the {@code IsNotNullExpression} labeled alternative + * in {@link FiltersParser#booleanExpression}. + * @param ctx the parse tree + */ + void enterIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx); + + /** + * Exit a parse tree produced by the {@code IsNotNullExpression} labeled alternative + * in {@link FiltersParser#booleanExpression}. + * @param ctx the parse tree + */ + void exitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx); + /** * Enter a parse tree produced by the {@code AndExpression} labeled alternative in * {@link FiltersParser#booleanExpression}. diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersParser.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersParser.java index 945a3a95334..6dfb9c510ef 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersParser.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,8 +49,8 @@ public class FiltersParser extends Parser { public static final int WHERE = 1, DOT = 2, COMMA = 3, LEFT_SQUARE_BRACKETS = 4, RIGHT_SQUARE_BRACKETS = 5, LEFT_PARENTHESIS = 6, RIGHT_PARENTHESIS = 7, EQUALS = 8, MINUS = 9, PLUS = 10, GT = 11, GE = 12, LT = 13, - LE = 14, NE = 15, AND = 16, OR = 17, IN = 18, NIN = 19, NOT = 20, BOOLEAN_VALUE = 21, QUOTED_STRING = 22, - INTEGER_VALUE = 23, DECIMAL_VALUE = 24, IDENTIFIER = 25, WS = 26; + LE = 14, NE = 15, AND = 16, OR = 17, IN = 18, NIN = 19, NOT = 20, IS = 21, NULL = 22, BOOLEAN_VALUE = 23, + QUOTED_STRING = 24, INTEGER_VALUE = 25, DECIMAL_VALUE = 26, IDENTIFIER = 27, WS = 28; public static final int RULE_where = 0, RULE_booleanExpression = 1, RULE_constantArray = 2, RULE_compare = 3, RULE_identifier = 4, RULE_constant = 5; @@ -63,61 +63,66 @@ public class FiltersParser extends Parser { @Deprecated public static final String[] tokenNames; - public static final String _serializedATN = "\u0004\u0001\u001aY\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002" + public static final String _serializedATN = "\u0004\u0001\u001cb\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002" + "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002" + "\u0005\u0007\u0005\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000\u0001" + "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" + "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" + "\u0001\u0003\u0001\u001e\b\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" - + "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0001(\b" + "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" - + "\u0001\u0005\u00010\b\u0001\n\u0001\f\u00013\t\u0001\u0001\u0002\u0001" - + "\u0002\u0001\u0002\u0001\u0002\u0005\u00029\b\u0002\n\u0002\f\u0002<\t" - + "\u0002\u0001\u0002\u0001\u0002\u0001\u0003\u0001\u0003\u0001\u0004\u0001" - + "\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0003\u0004G\b\u0004\u0001" - + "\u0005\u0003\u0005J\b\u0005\u0001\u0005\u0001\u0005\u0003\u0005N\b\u0005" - + "\u0001\u0005\u0001\u0005\u0004\u0005R\b\u0005\u000b\u0005\f\u0005S\u0001" - + "\u0005\u0003\u0005W\b\u0005\u0001\u0005\u0000\u0001\u0002\u0006\u0000" - + "\u0002\u0004\u0006\b\n\u0000\u0002\u0002\u0000\b\b\u000b\u000f\u0001\u0000" - + "\t\nb\u0000\f\u0001\u0000\u0000\u0000\u0002\'\u0001\u0000\u0000\u0000" - + "\u00044\u0001\u0000\u0000\u0000\u0006?\u0001\u0000\u0000\u0000\bF\u0001" - + "\u0000\u0000\u0000\nV\u0001\u0000\u0000\u0000\f\r\u0005\u0001\u0000\u0000" - + "\r\u000e\u0003\u0002\u0001\u0000\u000e\u000f\u0005\u0000\u0000\u0001\u000f" - + "\u0001\u0001\u0000\u0000\u0000\u0010\u0011\u0006\u0001\uffff\uffff\u0000" - + "\u0011\u0012\u0003\b\u0004\u0000\u0012\u0013\u0003\u0006\u0003\u0000\u0013" - + "\u0014\u0003\n\u0005\u0000\u0014(\u0001\u0000\u0000\u0000\u0015\u0016" - + "\u0003\b\u0004\u0000\u0016\u0017\u0005\u0012\u0000\u0000\u0017\u0018\u0003" - + "\u0004\u0002\u0000\u0018(\u0001\u0000\u0000\u0000\u0019\u001d\u0003\b" - + "\u0004\u0000\u001a\u001b\u0005\u0014\u0000\u0000\u001b\u001e\u0005\u0012" - + "\u0000\u0000\u001c\u001e\u0005\u0013\u0000\u0000\u001d\u001a\u0001\u0000" - + "\u0000\u0000\u001d\u001c\u0001\u0000\u0000\u0000\u001e\u001f\u0001\u0000" - + "\u0000\u0000\u001f \u0003\u0004\u0002\u0000 (\u0001\u0000\u0000\u0000" - + "!\"\u0005\u0006\u0000\u0000\"#\u0003\u0002\u0001\u0000#$\u0005\u0007\u0000" - + "\u0000$(\u0001\u0000\u0000\u0000%&\u0005\u0014\u0000\u0000&(\u0003\u0002" - + "\u0001\u0001\'\u0010\u0001\u0000\u0000\u0000\'\u0015\u0001\u0000\u0000" - + "\u0000\'\u0019\u0001\u0000\u0000\u0000\'!\u0001\u0000\u0000\u0000\'%\u0001" - + "\u0000\u0000\u0000(1\u0001\u0000\u0000\u0000)*\n\u0004\u0000\u0000*+\u0005" - + "\u0010\u0000\u0000+0\u0003\u0002\u0001\u0005,-\n\u0003\u0000\u0000-.\u0005" - + "\u0011\u0000\u0000.0\u0003\u0002\u0001\u0004/)\u0001\u0000\u0000\u0000" - + "/,\u0001\u0000\u0000\u000003\u0001\u0000\u0000\u00001/\u0001\u0000\u0000" - + "\u000012\u0001\u0000\u0000\u00002\u0003\u0001\u0000\u0000\u000031\u0001" - + "\u0000\u0000\u000045\u0005\u0004\u0000\u00005:\u0003\n\u0005\u000067\u0005" - + "\u0003\u0000\u000079\u0003\n\u0005\u000086\u0001\u0000\u0000\u00009<\u0001" - + "\u0000\u0000\u0000:8\u0001\u0000\u0000\u0000:;\u0001\u0000\u0000\u0000" - + ";=\u0001\u0000\u0000\u0000<:\u0001\u0000\u0000\u0000=>\u0005\u0005\u0000" - + "\u0000>\u0005\u0001\u0000\u0000\u0000?@\u0007\u0000\u0000\u0000@\u0007" - + "\u0001\u0000\u0000\u0000AB\u0005\u0019\u0000\u0000BC\u0005\u0002\u0000" - + "\u0000CG\u0005\u0019\u0000\u0000DG\u0005\u0019\u0000\u0000EG\u0005\u0016" - + "\u0000\u0000FA\u0001\u0000\u0000\u0000FD\u0001\u0000\u0000\u0000FE\u0001" - + "\u0000\u0000\u0000G\t\u0001\u0000\u0000\u0000HJ\u0007\u0001\u0000\u0000" - + "IH\u0001\u0000\u0000\u0000IJ\u0001\u0000\u0000\u0000JK\u0001\u0000\u0000" - + "\u0000KW\u0005\u0017\u0000\u0000LN\u0007\u0001\u0000\u0000ML\u0001\u0000" - + "\u0000\u0000MN\u0001\u0000\u0000\u0000NO\u0001\u0000\u0000\u0000OW\u0005" - + "\u0018\u0000\u0000PR\u0005\u0016\u0000\u0000QP\u0001\u0000\u0000\u0000" - + "RS\u0001\u0000\u0000\u0000SQ\u0001\u0000\u0000\u0000ST\u0001\u0000\u0000" - + "\u0000TW\u0001\u0000\u0000\u0000UW\u0005\u0015\u0000\u0000VI\u0001\u0000" - + "\u0000\u0000VM\u0001\u0000\u0000\u0000VQ\u0001\u0000\u0000\u0000VU\u0001" - + "\u0000\u0000\u0000W\u000b\u0001\u0000\u0000\u0000\n\u001d\'/1:FIMSV"; + + "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" + + "\u0001\u0001\u0001\u0003\u00011\b\u0001\u0001\u0001\u0001\u0001\u0001" + + "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u00019\b\u0001\n\u0001" + + "\f\u0001<\t\u0001\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0005" + + "\u0002B\b\u0002\n\u0002\f\u0002E\t\u0002\u0001\u0002\u0001\u0002\u0001" + + "\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001" + + "\u0004\u0003\u0004P\b\u0004\u0001\u0005\u0003\u0005S\b\u0005\u0001\u0005" + + "\u0001\u0005\u0003\u0005W\b\u0005\u0001\u0005\u0001\u0005\u0004\u0005" + + "[\b\u0005\u000b\u0005\f\u0005\\\u0001\u0005\u0003\u0005`\b\u0005\u0001" + + "\u0005\u0000\u0001\u0002\u0006\u0000\u0002\u0004\u0006\b\n\u0000\u0002" + + "\u0002\u0000\b\b\u000b\u000f\u0001\u0000\t\nm\u0000\f\u0001\u0000\u0000" + + "\u0000\u00020\u0001\u0000\u0000\u0000\u0004=\u0001\u0000\u0000\u0000\u0006" + + "H\u0001\u0000\u0000\u0000\bO\u0001\u0000\u0000\u0000\n_\u0001\u0000\u0000" + + "\u0000\f\r\u0005\u0001\u0000\u0000\r\u000e\u0003\u0002\u0001\u0000\u000e" + + "\u000f\u0005\u0000\u0000\u0001\u000f\u0001\u0001\u0000\u0000\u0000\u0010" + + "\u0011\u0006\u0001\uffff\uffff\u0000\u0011\u0012\u0003\b\u0004\u0000\u0012" + + "\u0013\u0003\u0006\u0003\u0000\u0013\u0014\u0003\n\u0005\u0000\u00141" + + "\u0001\u0000\u0000\u0000\u0015\u0016\u0003\b\u0004\u0000\u0016\u0017\u0005" + + "\u0012\u0000\u0000\u0017\u0018\u0003\u0004\u0002\u0000\u00181\u0001\u0000" + + "\u0000\u0000\u0019\u001d\u0003\b\u0004\u0000\u001a\u001b\u0005\u0014\u0000" + + "\u0000\u001b\u001e\u0005\u0012\u0000\u0000\u001c\u001e\u0005\u0013\u0000" + + "\u0000\u001d\u001a\u0001\u0000\u0000\u0000\u001d\u001c\u0001\u0000\u0000" + + "\u0000\u001e\u001f\u0001\u0000\u0000\u0000\u001f \u0003\u0004\u0002\u0000" + + " 1\u0001\u0000\u0000\u0000!\"\u0003\b\u0004\u0000\"#\u0005\u0015\u0000" + + "\u0000#$\u0005\u0016\u0000\u0000$1\u0001\u0000\u0000\u0000%&\u0003\b\u0004" + + "\u0000&\'\u0005\u0015\u0000\u0000\'(\u0005\u0014\u0000\u0000()\u0005\u0016" + + "\u0000\u0000)1\u0001\u0000\u0000\u0000*+\u0005\u0006\u0000\u0000+,\u0003" + + "\u0002\u0001\u0000,-\u0005\u0007\u0000\u0000-1\u0001\u0000\u0000\u0000" + + "./\u0005\u0014\u0000\u0000/1\u0003\u0002\u0001\u00010\u0010\u0001\u0000" + + "\u0000\u00000\u0015\u0001\u0000\u0000\u00000\u0019\u0001\u0000\u0000\u0000" + + "0!\u0001\u0000\u0000\u00000%\u0001\u0000\u0000\u00000*\u0001\u0000\u0000" + + "\u00000.\u0001\u0000\u0000\u00001:\u0001\u0000\u0000\u000023\n\u0004\u0000" + + "\u000034\u0005\u0010\u0000\u000049\u0003\u0002\u0001\u000556\n\u0003\u0000" + + "\u000067\u0005\u0011\u0000\u000079\u0003\u0002\u0001\u000482\u0001\u0000" + + "\u0000\u000085\u0001\u0000\u0000\u00009<\u0001\u0000\u0000\u0000:8\u0001" + + "\u0000\u0000\u0000:;\u0001\u0000\u0000\u0000;\u0003\u0001\u0000\u0000" + + "\u0000<:\u0001\u0000\u0000\u0000=>\u0005\u0004\u0000\u0000>C\u0003\n\u0005" + + "\u0000?@\u0005\u0003\u0000\u0000@B\u0003\n\u0005\u0000A?\u0001\u0000\u0000" + + "\u0000BE\u0001\u0000\u0000\u0000CA\u0001\u0000\u0000\u0000CD\u0001\u0000" + + "\u0000\u0000DF\u0001\u0000\u0000\u0000EC\u0001\u0000\u0000\u0000FG\u0005" + + "\u0005\u0000\u0000G\u0005\u0001\u0000\u0000\u0000HI\u0007\u0000\u0000" + + "\u0000I\u0007\u0001\u0000\u0000\u0000JK\u0005\u001b\u0000\u0000KL\u0005" + + "\u0002\u0000\u0000LP\u0005\u001b\u0000\u0000MP\u0005\u001b\u0000\u0000" + + "NP\u0005\u0018\u0000\u0000OJ\u0001\u0000\u0000\u0000OM\u0001\u0000\u0000" + + "\u0000ON\u0001\u0000\u0000\u0000P\t\u0001\u0000\u0000\u0000QS\u0007\u0001" + + "\u0000\u0000RQ\u0001\u0000\u0000\u0000RS\u0001\u0000\u0000\u0000ST\u0001" + + "\u0000\u0000\u0000T`\u0005\u0019\u0000\u0000UW\u0007\u0001\u0000\u0000" + + "VU\u0001\u0000\u0000\u0000VW\u0001\u0000\u0000\u0000WX\u0001\u0000\u0000" + + "\u0000X`\u0005\u001a\u0000\u0000Y[\u0005\u0018\u0000\u0000ZY\u0001\u0000" + + "\u0000\u0000[\\\u0001\u0000\u0000\u0000\\Z\u0001\u0000\u0000\u0000\\]" + + "\u0001\u0000\u0000\u0000]`\u0001\u0000\u0000\u0000^`\u0005\u0017\u0000" + + "\u0000_R\u0001\u0000\u0000\u0000_V\u0001\u0000\u0000\u0000_Z\u0001\u0000" + + "\u0000\u0000_^\u0001\u0000\u0000\u0000`\u000b\u0001\u0000\u0000\u0000\n\u001d08:CORV\\_"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); @@ -173,8 +178,8 @@ private static String[] makeLiteralNames() { private static String[] makeSymbolicNames() { return new String[] { null, "WHERE", "DOT", "COMMA", "LEFT_SQUARE_BRACKETS", "RIGHT_SQUARE_BRACKETS", "LEFT_PARENTHESIS", "RIGHT_PARENTHESIS", "EQUALS", "MINUS", "PLUS", "GT", "GE", "LT", "LE", "NE", "AND", - "OR", "IN", "NIN", "NOT", "BOOLEAN_VALUE", "QUOTED_STRING", "INTEGER_VALUE", "DECIMAL_VALUE", - "IDENTIFIER", "WS" }; + "OR", "IN", "NIN", "NOT", "IS", "NULL", "BOOLEAN_VALUE", "QUOTED_STRING", "INTEGER_VALUE", + "DECIMAL_VALUE", "IDENTIFIER", "WS" }; } @Override @@ -249,7 +254,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc int _alt; enterOuterAlt(_localctx, 1); { - setState(39); + setState(48); _errHandler.sync(this); switch (getInterpreter().adaptivePredict(_input, 1, _ctx)) { case 1: { @@ -306,30 +311,56 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc } break; case 4: { - _localctx = new GroupExpressionContext(_localctx); + _localctx = new IsNullExpressionContext(_localctx); _ctx = _localctx; _prevctx = _localctx; setState(33); - match(LEFT_PARENTHESIS); + identifier(); setState(34); - booleanExpression(0); + match(IS); setState(35); - match(RIGHT_PARENTHESIS); + match(NULL); } break; case 5: { - _localctx = new NotExpressionContext(_localctx); + _localctx = new IsNotNullExpressionContext(_localctx); _ctx = _localctx; _prevctx = _localctx; setState(37); - match(NOT); + identifier(); setState(38); + match(IS); + setState(39); + match(NOT); + setState(40); + match(NULL); + } + break; + case 6: { + _localctx = new GroupExpressionContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(42); + match(LEFT_PARENTHESIS); + setState(43); + booleanExpression(0); + setState(44); + match(RIGHT_PARENTHESIS); + } + break; + case 7: { + _localctx = new NotExpressionContext(_localctx); + _ctx = _localctx; + _prevctx = _localctx; + setState(46); + match(NOT); + setState(47); booleanExpression(1); } break; } _ctx.stop = _input.LT(-1); - setState(49); + setState(58); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input, 3, _ctx); while (_alt != 2 && _alt != org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER) { @@ -339,7 +370,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc } _prevctx = _localctx; { - setState(47); + setState(56); _errHandler.sync(this); switch (getInterpreter().adaptivePredict(_input, 2, _ctx)) { case 1: { @@ -347,13 +378,13 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc new BooleanExpressionContext(_parentctx, _parentState)); ((AndExpressionContext) _localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_booleanExpression); - setState(41); + setState(50); if (!(precpred(_ctx, 4))) { throw new FailedPredicateException(this, "precpred(_ctx, 4)"); } - setState(42); + setState(51); ((AndExpressionContext) _localctx).operator = match(AND); - setState(43); + setState(52); ((AndExpressionContext) _localctx).right = booleanExpression(5); } break; @@ -362,20 +393,20 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc new BooleanExpressionContext(_parentctx, _parentState)); ((OrExpressionContext) _localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_booleanExpression); - setState(44); + setState(53); if (!(precpred(_ctx, 3))) { throw new FailedPredicateException(this, "precpred(_ctx, 3)"); } - setState(45); + setState(54); ((OrExpressionContext) _localctx).operator = match(OR); - setState(46); + setState(55); ((OrExpressionContext) _localctx).right = booleanExpression(4); } break; } } } - setState(51); + setState(60); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input, 3, _ctx); } @@ -399,27 +430,27 @@ public final ConstantArrayContext constantArray() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(52); + setState(61); match(LEFT_SQUARE_BRACKETS); - setState(53); + setState(62); constant(); - setState(58); + setState(67); _errHandler.sync(this); _la = _input.LA(1); while (_la == COMMA) { { { - setState(54); + setState(63); match(COMMA); - setState(55); + setState(64); constant(); } } - setState(60); + setState(69); _errHandler.sync(this); _la = _input.LA(1); } - setState(61); + setState(70); match(RIGHT_SQUARE_BRACKETS); } } @@ -441,7 +472,7 @@ public final CompareContext compare() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(63); + setState(72); _la = _input.LA(1); if (!((((_la) & ~0x3f) == 0 && ((1L << _la) & 63744L) != 0))) { _errHandler.recoverInline(this); @@ -470,28 +501,28 @@ public final IdentifierContext identifier() throws RecognitionException { IdentifierContext _localctx = new IdentifierContext(_ctx, getState()); enterRule(_localctx, 8, RULE_identifier); try { - setState(70); + setState(79); _errHandler.sync(this); switch (getInterpreter().adaptivePredict(_input, 5, _ctx)) { case 1: enterOuterAlt(_localctx, 1); { - setState(65); + setState(74); match(IDENTIFIER); - setState(66); + setState(75); match(DOT); - setState(67); + setState(76); match(IDENTIFIER); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(68); + setState(77); match(IDENTIFIER); } break; case 3: enterOuterAlt(_localctx, 3); { - setState(69); + setState(78); match(QUOTED_STRING); } break; @@ -514,18 +545,18 @@ public final ConstantContext constant() throws RecognitionException { int _la; try { int _alt; - setState(86); + setState(95); _errHandler.sync(this); switch (getInterpreter().adaptivePredict(_input, 9, _ctx)) { case 1: _localctx = new IntegerConstantContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(73); + setState(82); _errHandler.sync(this); _la = _input.LA(1); if (_la == MINUS || _la == PLUS) { { - setState(72); + setState(81); _la = _input.LA(1); if (!(_la == MINUS || _la == PLUS)) { _errHandler.recoverInline(this); @@ -540,19 +571,19 @@ public final ConstantContext constant() throws RecognitionException { } } - setState(75); + setState(84); match(INTEGER_VALUE); } break; case 2: _localctx = new DecimalConstantContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(77); + setState(86); _errHandler.sync(this); _la = _input.LA(1); if (_la == MINUS || _la == PLUS) { { - setState(76); + setState(85); _la = _input.LA(1); if (!(_la == MINUS || _la == PLUS)) { _errHandler.recoverInline(this); @@ -567,21 +598,21 @@ public final ConstantContext constant() throws RecognitionException { } } - setState(79); + setState(88); match(DECIMAL_VALUE); } break; case 3: _localctx = new TextConstantContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(81); + setState(90); _errHandler.sync(this); _alt = 1; do { switch (_alt) { case 1: { { - setState(80); + setState(89); match(QUOTED_STRING); } } @@ -589,7 +620,7 @@ public final ConstantContext constant() throws RecognitionException { default: throw new NoViableAltException(this); } - setState(83); + setState(92); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input, 8, _ctx); } @@ -599,7 +630,7 @@ public final ConstantContext constant() throws RecognitionException { case 4: _localctx = new BooleanConstantContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(85); + setState(94); match(BOOLEAN_VALUE); } break; @@ -758,6 +789,100 @@ public T accept(ParseTreeVisitor visitor) { } + @SuppressWarnings("CheckReturnValue") + public static class IsNullExpressionContext extends BooleanExpressionContext { + + public IsNullExpressionContext(BooleanExpressionContext ctx) { + copyFrom(ctx); + } + + public IdentifierContext identifier() { + return getRuleContext(IdentifierContext.class, 0); + } + + public TerminalNode IS() { + return getToken(FiltersParser.IS, 0); + } + + public TerminalNode NULL() { + return getToken(FiltersParser.NULL, 0); + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof FiltersListener) { + ((FiltersListener) listener).enterIsNullExpression(this); + } + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof FiltersListener) { + ((FiltersListener) listener).exitIsNullExpression(this); + } + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof FiltersVisitor) { + return ((FiltersVisitor) visitor).visitIsNullExpression(this); + } + else { + return visitor.visitChildren(this); + } + } + + } + + @SuppressWarnings("CheckReturnValue") + public static class IsNotNullExpressionContext extends BooleanExpressionContext { + + public IsNotNullExpressionContext(BooleanExpressionContext ctx) { + copyFrom(ctx); + } + + public IdentifierContext identifier() { + return getRuleContext(IdentifierContext.class, 0); + } + + public TerminalNode IS() { + return getToken(FiltersParser.IS, 0); + } + + public TerminalNode NOT() { + return getToken(FiltersParser.NOT, 0); + } + + public TerminalNode NULL() { + return getToken(FiltersParser.NULL, 0); + } + + @Override + public void enterRule(ParseTreeListener listener) { + if (listener instanceof FiltersListener) { + ((FiltersListener) listener).enterIsNotNullExpression(this); + } + } + + @Override + public void exitRule(ParseTreeListener listener) { + if (listener instanceof FiltersListener) { + ((FiltersListener) listener).exitIsNotNullExpression(this); + } + } + + @Override + public T accept(ParseTreeVisitor visitor) { + if (visitor instanceof FiltersVisitor) { + return ((FiltersVisitor) visitor).visitIsNotNullExpression(this); + } + else { + return visitor.visitChildren(this); + } + } + + } + @SuppressWarnings("CheckReturnValue") public static class AndExpressionContext extends BooleanExpressionContext { diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersVisitor.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersVisitor.java index 887159c2b73..5757a12cbc3 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersVisitor.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/antlr4/FiltersVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,22 @@ public interface FiltersVisitor extends ParseTreeVisitor { */ T visitNinExpression(FiltersParser.NinExpressionContext ctx); + /** + * Visit a parse tree produced by the {@code IsNullExpression} labeled alternative in + * {@link FiltersParser#booleanExpression}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitIsNullExpression(FiltersParser.IsNullExpressionContext ctx); + + /** + * Visit a parse tree produced by the {@code IsNotNullExpression} labeled alternative + * in {@link FiltersParser#booleanExpression}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitIsNotNullExpression(FiltersParser.IsNotNullExpressionContext ctx); + /** * Visit a parse tree produced by the {@code AndExpression} labeled alternative in * {@link FiltersParser#booleanExpression}. diff --git a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/converter/AbstractFilterExpressionConverter.java b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/converter/AbstractFilterExpressionConverter.java index 3d63d121713..ec516c05118 100644 --- a/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/converter/AbstractFilterExpressionConverter.java +++ b/spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/filter/converter/AbstractFilterExpressionConverter.java @@ -77,8 +77,9 @@ else if (operand instanceof Filter.Value value) { } else if (operand instanceof Filter.Expression expression) { if ((expression.type() != ExpressionType.NOT && expression.type() != ExpressionType.AND - && expression.type() != ExpressionType.OR) && !(expression.right() instanceof Filter.Value)) { - throw new RuntimeException("Non AND/OR expression must have Value right argument!"); + && expression.type() != ExpressionType.OR) && !(expression.right() instanceof Filter.Value) + && !(expression.type() == ExpressionType.ISNULL || expression.type() == ExpressionType.ISNOTNULL)) { + throw new RuntimeException("Non AND/OR/ISNULL/ISNOTNULL expression must have Value right argument!"); } if (expression.type() == ExpressionType.NOT) { this.doNot(expression, context); diff --git a/spring-ai-vector-store/src/main/resources/antlr4/org/springframework/ai/vectorstore/filter/antlr4/Filters.g4 b/spring-ai-vector-store/src/main/resources/antlr4/org/springframework/ai/vectorstore/filter/antlr4/Filters.g4 index ba0f281c7c6..f43a15b052f 100644 --- a/spring-ai-vector-store/src/main/resources/antlr4/org/springframework/ai/vectorstore/filter/antlr4/Filters.g4 +++ b/spring-ai-vector-store/src/main/resources/antlr4/org/springframework/ai/vectorstore/filter/antlr4/Filters.g4 @@ -30,6 +30,8 @@ booleanExpression : identifier compare constant # CompareExpression | identifier IN constantArray # InExpression | identifier (NOT IN | NIN) constantArray # NinExpression + | identifier IS NULL # IsNullExpression + | identifier IS NOT NULL # IsNotNullExpression | left=booleanExpression operator=AND right=booleanExpression # AndExpression | left=booleanExpression operator=OR right=booleanExpression # OrExpression | LEFT_PARENTHESIS booleanExpression RIGHT_PARENTHESIS # GroupExpression @@ -78,6 +80,8 @@ OR: 'OR' | 'or' | '||'; IN: 'IN' | 'in'; NIN: 'NIN' | 'nin'; NOT: 'NOT' | 'not'; +IS: 'IS' | 'is'; +NULL: 'NULL' | 'null'; BOOLEAN_VALUE : 'TRUE' | 'true' | 'FALSE' | 'false' @@ -115,4 +119,4 @@ fragment LETTER WS : [ \r\n\t]+ -> channel(HIDDEN) - ; \ No newline at end of file + ; diff --git a/vector-stores/spring-ai-coherence-store/src/main/java/org/springframework/ai/vectorstore/coherence/CoherenceFilterExpressionConverter.java b/vector-stores/spring-ai-coherence-store/src/main/java/org/springframework/ai/vectorstore/coherence/CoherenceFilterExpressionConverter.java index 29f2f1311bc..d3d3416f9c8 100644 --- a/vector-stores/spring-ai-coherence-store/src/main/java/org/springframework/ai/vectorstore/coherence/CoherenceFilterExpressionConverter.java +++ b/vector-stores/spring-ai-coherence-store/src/main/java/org/springframework/ai/vectorstore/coherence/CoherenceFilterExpressionConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,7 @@ private Filter convert(Expression expression) { case NOT -> convert(FilterHelper.negate(expression)); case AND -> and(expression); case OR -> or(expression); + default -> throw new IllegalStateException("Unexpected value: " + expression.type()); }; } diff --git a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchAiSearchFilterExpressionConverter.java b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchAiSearchFilterExpressionConverter.java index eb8b505a8df..776194d47f2 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchAiSearchFilterExpressionConverter.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/main/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchAiSearchFilterExpressionConverter.java @@ -56,6 +56,15 @@ protected void doExpression(Expression expression, StringBuilder context) { this.convertOperand(expression.right(), context); context.append(")"); } + else if (expression.type() == Filter.ExpressionType.ISNULL) { + context.append("-"); + this.convertOperand(expression.left(), context); + context.append("*"); + } + else if (expression.type() == Filter.ExpressionType.ISNOTNULL) { + this.convertOperand(expression.left(), context); + context.append("*"); + } else { this.convertOperand(expression.left(), context); context.append(getOperationSymbol(expression)); diff --git a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java index f1f706539df..08aa445c101 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/elasticsearch/ElasticsearchVectorStoreIT.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -57,6 +58,7 @@ import org.springframework.ai.test.vectorstore.BaseVectorStoreTests; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @@ -414,6 +416,98 @@ public void searchThresholdTest(String vectorStoreBeanName) { }); } + @Test + public void searchWithIsNullFilter() { + getContextRunner().run(context -> { + ElasticsearchVectorStore vectorStore = context.getBean("vectorStore_cosine", + ElasticsearchVectorStore.class); + + var bgDocument = new Document("1", "The World is Big and Salvation Lurks Around the Corner", + Map.of("country", "BG", "year", 2020, "activationDate", new Date(1000))); + var nlDocument = new Document("2", "The World is Big and Salvation Lurks Around the Corner", + Map.of("country", "NL")); + var bgDocument2 = new Document("3", "The World is Big and Salvation Lurks Around the Corner", + Map.of("country", "BG", "year", 2023, "activationDate", new Date(3000))); + + vectorStore.add(List.of(bgDocument, nlDocument, bgDocument2)); + + Awaitility.await() + .until(() -> vectorStore.similaritySearch( + SearchRequest.builder().query("The World").topK(5).similarityThresholdAll().build()), + hasSize(3)); + + // with text filter expression + List resultWithText = vectorStore.similaritySearch(SearchRequest.builder() + .query("The World") + .topK(5) + .similarityThresholdAll() + .filterExpression("year IS NULL") + .build()); + + assertThat(resultWithText).hasSize(1); + assertThat(resultWithText.get(0).getId()).isEqualTo(nlDocument.getId()); + + // with filter expression builder + List resultsWithBuilder = vectorStore.similaritySearch(SearchRequest.builder() + .query("The World") + .topK(5) + .similarityThresholdAll() + .filterExpression(new FilterExpressionBuilder().isNull("year").build()) + .build()); + + assertThat(resultsWithBuilder).hasSize(1); + assertThat(resultsWithBuilder.get(0).getId()).isEqualTo(nlDocument.getId()); + }); + } + + @Test + public void searchWithIsNotNullFilter() { + getContextRunner().run(context -> { + ElasticsearchVectorStore vectorStore = context.getBean("vectorStore_cosine", + ElasticsearchVectorStore.class); + + var bgDocument = new Document("1", "The World is Big and Salvation Lurks Around the Corner", + Map.of("country", "BG", "year", 2020, "activationDate", new Date(1000))); + var nlDocument = new Document("2", "The World is Big and Salvation Lurks Around the Corner", + Map.of("country", "NL")); + var bgDocument2 = new Document("3", "The World is Big and Salvation Lurks Around the Corner", + Map.of("country", "BG", "year", 2023, "activationDate", new Date(3000))); + + vectorStore.add(List.of(bgDocument, nlDocument, bgDocument2)); + + Awaitility.await() + .until(() -> vectorStore.similaritySearch( + SearchRequest.builder().query("The World").topK(5).similarityThresholdAll().build()), + hasSize(3)); + + Set expectedResultSet = Set.of(bgDocument.getId(), bgDocument2.getId()); + + // with text filter expression + List resultWithText = vectorStore.similaritySearch(SearchRequest.builder() + .query("The World") + .topK(5) + .similarityThresholdAll() + .filterExpression("year IS NOT NULL") + .build()); + + assertThat(resultWithText).hasSize(2); + assertThat(resultWithText.get(0).getId()).isIn(expectedResultSet); + assertThat(resultWithText.get(1).getId()).isIn(expectedResultSet); + + // with filter expression builder + List resultsWithBuilder = vectorStore.similaritySearch(SearchRequest.builder() + .query("The World") + .topK(5) + .similarityThresholdAll() + .filterExpression(new FilterExpressionBuilder().isNotNull("year").build()) + .build()); + + assertThat(resultsWithBuilder).hasSize(2); + assertThat(resultsWithBuilder.get(0).getId()).isIn(expectedResultSet); + assertThat(resultsWithBuilder.get(1).getId()).isIn(expectedResultSet); + }); + } + @Test public void overDefaultSizeTest() {