{"record":{"id":"824809862cd158be","repo":"spring-projects/spring-ai","slug":"the-in-operator-should-have-been-transformed-int","errorCode":null,"errorMessage":"The 'IN' operator should have been transformed into chain of OR/EQ expressions.","messagePattern":"The 'IN' operator should have been transformed into chain of OR/EQ expressions\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/weaviate/WeaviateFilterExpressionConverter.java","lineNumber":118,"sourceCode":"\t\t}\n\t\telse {\n\t\t\tthis.convertOperand(exp.left(), context);\n\t\t\tcontext.append(getOperationSymbol(exp));\n\t\t\tthis.convertOperand(exp.right(), context);\n\t\t}\n\t}\n\n\tprivate String getOperationSymbol(Expression exp) {\n\t\treturn switch (exp.type()) {\n\t\t\tcase AND -> \"operator:And \\n\";\n\t\t\tcase OR -> \"operator:Or \\n\";\n\t\t\tcase EQ -> \"operator:Equal \\n\";\n\t\t\tcase NE -> \"operator:NotEqual \\n\";\n\t\t\tcase LT -> \"operator:LessThan \\n\";\n\t\t\tcase LTE -> \"operator:LessThanEqual \\n\";\n\t\t\tcase GT -> \"operator:GreaterThan \\n\";\n\t\t\tcase GTE -> \"operator:GreaterThanEqual \\n\";\n\t\t\tcase IN -> throw new IllegalStateException(\n\t\t\t\t\t\"The 'IN' operator should have been transformed into chain of OR/EQ expressions.\");\n\t\t\tcase NIN -> throw new IllegalStateException(\n\t\t\t\t\t\"The 'NIN' operator should have been transformed into chain of AND/NEQ expressions.\");\n\t\t\tdefault -> throw new UnsupportedOperationException(\"Not supported expression type:\" + exp.type());\n\t\t};\n\t}\n\n\t@Override\n\tprotected void doKey(Key key, StringBuilder context) {\n\t\tvar identifier = (hasOuterQuotes(key.key())) ? removeOuterQuotes(key.key()) : key.key();\n\t\tcontext.append(\"path:[\");\n\t\temitJsonValue(withMetaPrefix(identifier), context);\n\t\tcontext.append(\"] \\n\");\n\t}\n\n\tpublic String withMetaPrefix(String identifier) {\n\t\tif (SYSTEM_IDENTIFIERS.contains(identifier)) {\n\t\t\treturn identifier;","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/vector-stores/spring-ai-weaviate-store/src/main/java/org/springframework/ai/vectorstore/weaviate/WeaviateFilterExpressionConverter.java#L100-L136","documentation":"The Weaviate converter cannot emit IN directly: before conversion, IN expressions are (or must be) transformed into a chain of OR/EQ expressions, because Weaviate's GraphQL where filter has no native IN operator. Encountering IN in getOperationSymbol means the pre-transformation step didn't run, so an IllegalStateException is thrown.","triggerScenarios":"Calling the converter with a Filter.Expression of type IN that bypassed the standard doExpression transformation, e.g. invoking getOperationSymbol directly or a code path that skips the IN-expansion preprocessing.","commonSituations":"Custom code calling the converter internals directly; a subclass overriding doExpression and removing the IN expansion; version mismatch where the transformation was expected upstream.","solutions":["Let the standard conversion path (convert/doExpression) handle the expression — it expands IN into OR of EQ automatically.","Manually rewrite your filter as OR of EQ expressions: (a==1 OR a==2 OR a==3).","If you override doExpression in a subclass, ensure you still expand IN/NIN before delegating to getOperationSymbol."],"exampleFix":"// before\nvar expr = new Filter.Expression(IN, new Key(\"color\"), new Value(List.of(\"red\", \"blue\"))); // passed straight to operand conversion\n// after\nvar or1 = new Filter.Expression(EQ, new Key(\"color\"), new Value(\"red\"));\nvar or2 = new Filter.Expression(EQ, new Key(\"color\"), new Value(\"blue\"));\nvar expr = new Filter.Expression(OR, or1, or2);","handlingStrategy":"type-guard","validationCode":"if (expr.type() == Filter.ExpressionType.IN) { throw new IllegalArgumentException(\"Use OR/EQ chains or the standard conversion path for IN\"); }","typeGuard":"static boolean requiresExpansion(Filter.Expression e) { return e.type() == Filter.ExpressionType.IN || e.type() == Filter.ExpressionType.NIN; }","tryCatchPattern":"try { String q = converter.convertExpression(expr); } catch (IllegalStateException e) { if (e.getMessage().contains(\"'IN' operator\")) { /* expand IN into OR/EQ yourself */ } else throw e; }","preventionTips":["Always convert through the full converter pipeline, never call getOperationSymbol directly","Express set membership with IN and let the converter expand it","Avoid subclass overrides that skip the IN expansion step"],"tags":["vector-store","filter-expression","weaviate","in-operator","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}