From 6a3c54888385a91168f8408c43f428bc469e30f5 Mon Sep 17 00:00:00 2001 From: Oleksandr Klymenko <> Date: Tue, 5 Nov 2024 00:18:40 +0200 Subject: [PATCH] Refactor to modern switch expressions in vector stores Replace traditional switch statements with Java 14 switch expressions across vector store filter converters and related components. This change improves code quality in our filter expression handling for Azure, Milvus, Redis, Typesense and Weaviate implementations. The switch expressions eliminate fall-through behavior, enforce exhaustive pattern matching at compile time, and provide a more direct way to return values. This makes the filter conversion logic more robust and maintainable. --- ...zureAiSearchFilterExpressionConverter.java | 37 ++++++----------- .../MilvusFilterExpressionConverter.java | 37 ++++++----------- .../RedisFilterExpressionConverter.java | 39 +++++++----------- .../ai/vectorstore/RedisVectorStore.java | 18 ++++---- .../TypesenseFilterExpressionConverter.java | 37 ++++++----------- .../WeaviateFilterExpressionConverter.java | 41 +++++++------------ 6 files changed, 76 insertions(+), 133 deletions(-) diff --git a/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java index a04aa40b1..545edf359 100644 --- a/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java +++ b/vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureAiSearchFilterExpressionConverter.java @@ -80,30 +80,19 @@ public class AzureAiSearchFilterExpressionConverter extends AbstractFilterExpres } private String getOperationSymbol(Expression exp) { - switch (exp.type()) { - case AND: - return " and "; - case OR: - return " or "; - case EQ: - return " eq "; - case NE: - return " ne "; - case LT: - return " lt "; - case LTE: - return " le "; - case GT: - return " gt "; - case GTE: - return " ge "; - case IN: - return " search.in"; - case NIN: - return " not search.in"; - default: - throw new RuntimeException("Not supported expression type: " + exp.type()); - } + return switch (exp.type()) { + case AND -> " and "; + case OR -> " or "; + case EQ -> " eq "; + case NE -> " ne "; + case LT -> " lt "; + case LTE -> " le "; + case GT -> " gt "; + case GTE -> " ge "; + case IN -> " search.in"; + case NIN -> " not search.in"; + default -> throw new RuntimeException("Not supported expression type: " + exp.type()); + }; } @Override diff --git a/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusFilterExpressionConverter.java b/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusFilterExpressionConverter.java index 2a27e487c..d8ded6c2e 100644 --- a/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusFilterExpressionConverter.java +++ b/vector-stores/spring-ai-milvus-store/src/main/java/org/springframework/ai/vectorstore/MilvusFilterExpressionConverter.java @@ -38,30 +38,19 @@ public class MilvusFilterExpressionConverter extends AbstractFilterExpressionCon } private String getOperationSymbol(Expression exp) { - switch (exp.type()) { - case AND: - return " && "; - case OR: - return " || "; - case EQ: - return " == "; - case NE: - return " != "; - case LT: - return " < "; - case LTE: - return " <= "; - case GT: - return " > "; - case GTE: - return " >= "; - case IN: - return " in "; - case NIN: - return " not in "; - default: - throw new RuntimeException("Not supported expression type:" + exp.type()); - } + return switch (exp.type()) { + case AND -> " && "; + case OR -> " || "; + case EQ -> " == "; + case NE -> " != "; + case LT -> " < "; + case LTE -> " <= "; + case GT -> " > "; + case GTE -> " >= "; + case IN -> " in "; + case NIN -> " not in "; + default -> throw new RuntimeException("Not supported expression type:" + exp.type()); + }; } @Override diff --git a/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java index 86638c071..4536eda08 100644 --- a/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java +++ b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisFilterExpressionConverter.java @@ -140,33 +140,24 @@ public class RedisFilterExpressionConverter extends AbstractFilterExpressionConv } private String tagValueDelimiter(Expression expression) { - switch (expression.type()) { - case IN: - return " | "; - case EQ: - return " "; - default: - throw new UnsupportedOperationException( - MessageFormat.format("Tag operand {0} not supported", expression.type())); - } + return switch (expression.type()) { + case IN -> " | "; + case EQ -> " "; + default -> throw new UnsupportedOperationException( + MessageFormat.format("Tag operand {0} not supported", expression.type())); + }; } private Numeric numeric(Expression expression, Value value) { - switch (expression.type()) { - case EQ: - return new Numeric(inclusive(value), inclusive(value)); - case GT: - return new Numeric(exclusive(value), POSITIVE_INFINITY); - case GTE: - return new Numeric(inclusive(value), POSITIVE_INFINITY); - case LT: - return new Numeric(NEGATIVE_INFINITY, exclusive(value)); - case LTE: - return new Numeric(NEGATIVE_INFINITY, inclusive(value)); - default: - throw new UnsupportedOperationException(MessageFormat - .format("Expression type {0} not supported for numeric fields", expression.type())); - } + return switch (expression.type()) { + case EQ -> new Numeric(inclusive(value), inclusive(value)); + case GT -> new Numeric(exclusive(value), POSITIVE_INFINITY); + case GTE -> new Numeric(inclusive(value), POSITIVE_INFINITY); + case LT -> new Numeric(NEGATIVE_INFINITY, exclusive(value)); + case LTE -> new Numeric(NEGATIVE_INFINITY, inclusive(value)); + default -> throw new UnsupportedOperationException( + MessageFormat.format("Expression type {0} not supported for numeric fields", expression.type())); + }; } private NumericBoundary inclusive(Value value) { diff --git a/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java index 0d3900533..df2770671 100644 --- a/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java +++ b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java @@ -305,17 +305,13 @@ public class RedisVectorStore extends AbstractObservationVectorStore implements private SchemaField schemaField(MetadataField field) { String fieldName = jsonPath(field.name); - switch (field.fieldType) { - case NUMERIC: - return NumericField.of(fieldName).as(field.name); - case TAG: - return TagField.of(fieldName).as(field.name); - case TEXT: - return TextField.of(fieldName).as(field.name); - default: - throw new IllegalArgumentException( - MessageFormat.format("Field {0} has unsupported type {1}", field.name, field.fieldType)); - } + return switch (field.fieldType) { + case NUMERIC -> NumericField.of(fieldName).as(field.name); + case TAG -> TagField.of(fieldName).as(field.name); + case TEXT -> TextField.of(fieldName).as(field.name); + default -> throw new IllegalArgumentException( + MessageFormat.format("Field {0} has unsupported type {1}", field.name, field.fieldType)); + }; } private VectorAlgorithm vectorAlgorithm() { diff --git a/vector-stores/spring-ai-typesense-store/src/main/java/org/springframework/ai/vectorstore/TypesenseFilterExpressionConverter.java b/vector-stores/spring-ai-typesense-store/src/main/java/org/springframework/ai/vectorstore/TypesenseFilterExpressionConverter.java index e3decb870..8c0a2140b 100644 --- a/vector-stores/spring-ai-typesense-store/src/main/java/org/springframework/ai/vectorstore/TypesenseFilterExpressionConverter.java +++ b/vector-stores/spring-ai-typesense-store/src/main/java/org/springframework/ai/vectorstore/TypesenseFilterExpressionConverter.java @@ -35,31 +35,20 @@ public class TypesenseFilterExpressionConverter extends AbstractFilterExpression } private String getOperationSymbol(Filter.Expression exp) { - switch (exp.type()) { - case AND: - return " && "; - case OR: - return " || "; - case EQ: - return " "; // in typesense "EQ" operator looks like -> country:USA - case NE: - return " != "; - case LT: - return " < "; - case LTE: - return " <= "; - case GT: - return " > "; - case GTE: - return " >= "; - case IN: - return " "; // in typesense "IN" operator looks like -> country: [USA, UK] - case NIN: - return " != "; // in typesense "NIN" operator looks like -> country: + return switch (exp.type()) { + case AND -> " && "; + case OR -> " || "; + case EQ -> " "; // in typesense "EQ" operator looks like -> country:USA + case NE -> " != "; + case LT -> " < "; + case LTE -> " <= "; + case GT -> " > "; + case GTE -> " >= "; + case IN -> " "; // in typesense "IN" operator looks like -> country: [USA, UK] + case NIN -> " != "; // in typesense "NIN" operator looks like -> country: // !=[USA, UK] - default: - throw new RuntimeException("Not supported expression type:" + exp.type()); - } + default -> throw new RuntimeException("Not supported expression type:" + exp.type()); + }; } @Override diff --git a/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java b/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java index 08eb2f29f..2f368d678 100644 --- a/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java +++ b/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/WeaviateFilterExpressionConverter.java @@ -83,32 +83,21 @@ public class WeaviateFilterExpressionConverter extends AbstractFilterExpressionC } private String getOperationSymbol(Expression exp) { - switch (exp.type()) { - case AND: - return "operator:And \n"; - case OR: - return "operator:Or \n"; - case EQ: - return "operator:Equal \n"; - case NE: - return "operator:NotEqual \n"; - case LT: - return "operator:LessThan \n"; - case LTE: - return "operator:LessThanEqual \n"; - case GT: - return "operator:GreaterThan \n"; - case GTE: - return "operator:GreaterThanEqual \n"; - case IN: - throw new IllegalStateException( - "The 'IN' operator should have been transformed into chain of OR/EQ expressions."); - case NIN: - throw new IllegalStateException( - "The 'NIN' operator should have been transformed into chain of AND/NEQ expressions."); - default: - throw new UnsupportedOperationException("Not supported expression type:" + exp.type()); - } + return switch (exp.type()) { + case AND -> "operator:And \n"; + case OR -> "operator:Or \n"; + case EQ -> "operator:Equal \n"; + case NE -> "operator:NotEqual \n"; + case LT -> "operator:LessThan \n"; + case LTE -> "operator:LessThanEqual \n"; + case GT -> "operator:GreaterThan \n"; + case GTE -> "operator:GreaterThanEqual \n"; + case IN -> throw new IllegalStateException( + "The 'IN' operator should have been transformed into chain of OR/EQ expressions."); + case NIN -> throw new IllegalStateException( + "The 'NIN' operator should have been transformed into chain of AND/NEQ expressions."); + default -> throw new UnsupportedOperationException("Not supported expression type:" + exp.type()); + }; } @Override