From 6f9e294b6b6b59f4a1d955540f03e8aac6a84fe9 Mon Sep 17 00:00:00 2001 From: Alex Klimenko Date: Thu, 31 Jul 2025 19:22:42 +0200 Subject: [PATCH] Add comprehensive test coverage for Neo4jVectorFilterExpressionConverter Signed-off-by: Alex Klimenko --- ...jVectorFilterExpressionConverterTests.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverterTests.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverterTests.java index aeb6c01f435..0832ba0182d 100644 --- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverterTests.java +++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverterTests.java @@ -139,4 +139,115 @@ public void testComplexIdentifiers2() { .isEqualTo("node.`metadata.author` IN [\"john\",\"jill\"] AND node.`metadata.'article_type'` = \"blog\""); } + @Test + public void testEmptyList() { + // category IN [] + String vectorExpr = this.converter + .convertExpression(new Expression(IN, new Key("category"), new Value(List.of()))); + assertThat(vectorExpr).isEqualTo("node.`metadata.category` IN []"); + } + + @Test + public void testSingleItemList() { + // status IN ["active"] + String vectorExpr = this.converter + .convertExpression(new Expression(IN, new Key("status"), new Value(List.of("active")))); + assertThat(vectorExpr).isEqualTo("node.`metadata.status` IN [\"active\"]"); + } + + @Test + public void testNullValue() { + // description = null + String vectorExpr = this.converter + .convertExpression(new Expression(EQ, new Key("description"), new Value(null))); + assertThat(vectorExpr).isEqualTo("node.`metadata.description` = null"); + } + + @Test + public void testNestedJsonPath() { + // entity.profile.name = "EntityA" + String vectorExpr = this.converter + .convertExpression(new Expression(EQ, new Key("entity.profile.name"), new Value("EntityA"))); + assertThat(vectorExpr).isEqualTo("node.`metadata.entity.profile.name` = \"EntityA\""); + } + + @Test + public void testNumericStringValue() { + // id = "1" + String vectorExpr = this.converter.convertExpression(new Expression(EQ, new Key("id"), new Value("1"))); + assertThat(vectorExpr).isEqualTo("node.`metadata.id` = \"1\""); + } + + @Test + public void testZeroValue() { + // count = 0 + String vectorExpr = this.converter.convertExpression(new Expression(EQ, new Key("count"), new Value(0))); + assertThat(vectorExpr).isEqualTo("node.`metadata.count` = 0"); + } + + @Test + public void testComplexNestedGroups() { + // ((fieldA >= 100 AND fieldB = "X1") OR (fieldA >= 50 AND fieldB = "Y2")) AND + // fieldC <> "inactive" + String vectorExpr = this.converter.convertExpression(new Expression(AND, + new Group(new Expression(OR, + new Group(new Expression(AND, new Expression(GTE, new Key("fieldA"), new Value(100)), + new Expression(EQ, new Key("fieldB"), new Value("X1")))), + new Group(new Expression(AND, new Expression(GTE, new Key("fieldA"), new Value(50)), + new Expression(EQ, new Key("fieldB"), new Value("Y2")))))), + new Expression(NE, new Key("fieldC"), new Value("inactive")))); + + assertThat(vectorExpr).isEqualTo("((node.`metadata.fieldA` >= 100 AND node.`metadata.fieldB` = \"X1\") OR " + + "(node.`metadata.fieldA` >= 50 AND node.`metadata.fieldB` = \"Y2\")) AND " + + "node.`metadata.fieldC` <> \"inactive\""); + } + + @Test + public void testMixedDataTypes() { + // active = true AND score >= 1.5 AND tags IN ["featured", "premium"] AND version + // = 1 + String vectorExpr = this.converter.convertExpression(new Expression(AND, + new Expression(AND, + new Expression(AND, new Expression(EQ, new Key("active"), new Value(true)), + new Expression(GTE, new Key("score"), new Value(1.5))), + new Expression(IN, new Key("tags"), new Value(List.of("featured", "premium")))), + new Expression(EQ, new Key("version"), new Value(1)))); + + assertThat(vectorExpr).isEqualTo("node.`metadata.active` = true AND node.`metadata.score` >= 1.5 AND " + + "node.`metadata.tags` IN [\"featured\",\"premium\"] AND node.`metadata.version` = 1"); + } + + @Test + public void testNinWithMixedTypes() { + // status NOT IN ["A", "B", "C"] + String vectorExpr = this.converter + .convertExpression(new Expression(NIN, new Key("status"), new Value(List.of("A", "B", "C")))); + assertThat(vectorExpr).isEqualTo("NOT node.`metadata.status` IN [\"A\",\"B\",\"C\"]"); + } + + @Test + public void testEmptyStringValue() { + // description <> "" + String vectorExpr = this.converter.convertExpression(new Expression(NE, new Key("description"), new Value(""))); + assertThat(vectorExpr).isEqualTo("node.`metadata.description` <> \"\""); + } + + @Test + public void testArrayIndexAccess() { + // tags[0] = "important" + String vectorExpr = this.converter + .convertExpression(new Expression(EQ, new Key("tags[0]"), new Value("important"))); + assertThat(vectorExpr).isEqualTo("node.`metadata.tags[0]` = \"important\""); + } + + @Test + public void testNegativeNumbers() { + // valueA <= -5 AND valueB >= -10 + String vectorExpr = this.converter + .convertExpression(new Expression(AND, new Expression(LTE, new Key("valueA"), new Value(-5)), + new Expression(GTE, new Key("valueB"), new Value(-10)))); + + assertThat(vectorExpr).isEqualTo("node.`metadata.valueA` <= -5 AND node.`metadata.valueB` >= -10"); + } + }