spring-projects/spring-ai · error · UnsupportedOperationException
Expression type %s not yet implemented. Patches welcome.
Error message
Expression type %s not yet implemented. Patches welcome.
What it means
The Cassandra filter expression converter translates Spring AI Filter.Expression types into CQL. doOperand throws this UnsupportedOperationException when it encounters an expression type it cannot render as a CQL operator (e.g. CONTAINS/CONTAINS_KEY, which are commented out pending collection support). It signals an unsupported filter feature, not a runtime fault.
Source
Thrown at vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/cassandra/CassandraFilterExpressionConverter.java:69
this.columnsByName = columns.stream()
.collect(Collectors.toMap(c -> c.getName().asInternal(), Function.identity()));
}
private static void doOperand(ExpressionType type, StringBuilder context) {
switch (type) {
case EQ -> context.append(" = ");
case NE -> context.append(" != ");
case GT -> context.append(" > ");
case GTE -> context.append(" >= ");
case IN -> context.append(" IN ");
case LT -> context.append(" < ");
case LTE -> context.append(" <= ");
// TODO SAI supports collections
// reach out to mck@apache.org if you'd like these implemented
// case CONTAINS -> context.append(" CONTAINS ");
// case CONTAINS_KEY -> context.append(" CONTAINS_KEY ");
default -> throw new UnsupportedOperationException(
String.format("Expression type %s not yet implemented. Patches welcome.", type));
}
}
@Override
protected void doKey(Key key, StringBuilder context) {
String keyName = key.key();
Optional<ColumnMetadata> column = getColumn(keyName);
Preconditions.checkArgument(column.isPresent(), "No metafield %s has been configured", keyName);
context.append(column.get().getName().asCql(false));
}
@Override
protected void doExpression(Filter.Expression expression, StringBuilder context) {
switch (expression.type()) {
case AND -> doBinaryOperation(" and ", expression, context);
case OR -> doBinaryOperation(" or ", expression, context);
case NIN, NOT -> throw new UnsupportedOperationException(View on GitHub (pinned to 98a7beda4f)
Solutions
- Remove CONTAINS/CONTAINS_KEY predicates from your filter; restructure metadata so equality/range/IN comparisons suffice (e.g. store collection membership as separate boolean fields).
- Filter collections client-side: issue the Cassandra query without the unsupported predicate and post-filter the returned Documents in Java.
- Contribute the missing operator: implement the commented-out CONTAINS branches in CassandraFilterExpressionConverter.doOperand using Cassandra SAI CONTAINS syntax.
Example fix
// before
Filter.expr("metadata.tags").contains("ai")
// after (client-side filtering)
var docs = vectorStore.similaritySearch(SearchRequest.query(q).withFilterExpression(Filter.expr("metadata.topic").eq("ai")));
docs = docs.stream().filter(d -> d.getMetadata().get("tags", List.of()).contains("ai")).toList(); Defensive patterns
Strategy: validation
Validate before calling
Set<Filter.ExpressionType> SUPPORTED = Set.of(EQ, NE, GT, GTE, LT, LTE, IN, AND, OR);
void validate(Filter.Expression e) {
if (!SUPPORTED.contains(e.type())) throw new IllegalArgumentException("Unsupported for Cassandra: " + e.type());
if (e.left() instanceof Filter.Expression l) validate(l);
if (e.right() instanceof Filter.Expression r) validate(r);
} Type guard
boolean isCassandraSupported(Filter.Expression e) {
return e.type() == EQ || e.type() == NE || e.type() == GT || e.type() == GTE
|| e.type() == LT || e.type() == LTE || e.type() == IN || e.type() == AND || e.type() == OR;
} Try / catch
try {
vectorStore.similaritySearch(request);
} catch (UnsupportedOperationException e) {
// fall back to an unfiltered search plus client-side filtering
} Prevention
- Restrict filter DSLs for Cassandra to comparison, IN and boolean operators.
- Never use CONTAINS/CONTAINS_KEY filters with the Cassandra store.
- Unit-test every dynamically built filter against a support matrix before shipping.
When it happens
Trigger: Building a Filter.ExpressionBuilder filter containing an expression type other than EQ, NE, GT, GTE, LT, LTE, IN, AND, OR — specifically CONTAINS or CONTAINS_KEY on a metadata field — and passing it to CassandraVectorStore similaritySearch or delete with a FilterExpressionTextParser-generated expression.
Common situations: Developers port filters written for PgVector or other stores that support CONTAINS on arrays; using filter expressions with collection/JSON metadata fields against Cassandra where SAI collection operators are not yet wired up.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cassandra uses a custom doValue(ColumnMetadata, Object, Stri
- Unexpected value: {expressionType}
- Not supported expression type: {expressionType}
- Not supported expression type: {expressionType}
- Not supported expression type: {expressionType}
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/088008e088793b1f.
Report an issue: GitHub.