{"record":{"id":"67c82e6ea0c70837","repo":"spring-projects/spring-ai","slug":"the-nin-operator-should-have-been-transformed-in","errorCode":null,"errorMessage":"The 'NIN' operator should have been transformed into chain of AND/NEQ expressions.","messagePattern":"The 'NIN' operator should have been transformed into chain of AND/NEQ 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":120,"sourceCode":"\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;\n\t\t}\n","sourceCodeStart":102,"sourceCodeEnd":138,"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#L102-L138","documentation":"Analogous to IN, the NIN operator has no native Weaviate equivalent and must be transformed into a chain of AND/NEQ expressions before the operator symbol is generated. getOperationSymbol throws this IllegalStateException if it still sees an NIN expression, meaning the pre-transformation was skipped.","triggerScenarios":"Converting a Filter.Expression of type NIN without letting the converter's doExpression preprocessing expand it, e.g. direct calls to getOperationSymbol or subclass overrides that bypass the expansion.","commonSituations":"Direct/converter-level usage bypassing the standard pipeline; custom subclasses overriding doExpression; hand-constructed expression trees passed to a partial conversion step.","solutions":["Use the full standard conversion path so NIN is expanded into AND of NEQ automatically.","Manually rewrite the filter as AND of NEQ expressions: (a!=1 AND a!=2).","Fix subclass overrides of doExpression to preserve the NIN-to-AND/NEQ expansion."],"exampleFix":"// before\nvar expr = new Filter.Expression(NIN, new Key(\"status\"), new Value(List.of(\"draft\")));\n// after\nvar ne1 = new Filter.Expression(NE, new Key(\"status\"), new Value(\"draft\"));\nvar ne2 = new Filter.Expression(NE, new Key(\"status\"), new Value(\"archived\"));\nvar expr = new Filter.Expression(AND, ne1, ne2);","handlingStrategy":"type-guard","validationCode":"if (expr.type() == Filter.ExpressionType.NIN) { throw new IllegalArgumentException(\"Use AND/NEQ chains or the standard conversion path for NIN\"); }","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(\"'NIN' operator\")) { /* expand NIN into AND/NEQ yourself */ } else throw e; }","preventionTips":["Route NIN through the standard converter path which expands it","Never invoke getOperationSymbol on raw NIN expressions","Keep doExpression overrides expansion-preserving"],"tags":["vector-store","filter-expression","weaviate","nin-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"}