Overview
@Filter performs application-side filtering in Java. All events arrive from MongoDB, and a
predicate decides which ones are forwarded to handler methods. Events that don’t pass the filter
are silently skipped — but their resume token is still checkpointed as lastSeenToken.
Unlike @Pipeline which runs a MongoDB aggregation pipeline once at
stream startup to reduce network traffic, @Filter executes on every event and can leverage
Spring beans, service calls, and any Java logic.
At most one
@Filter method is allowed per @ChangeStream class.Supported Signatures
| Return type | Parameters | Description |
|---|---|---|
Predicate<ChangeStreamContext<?>> | None | Returns a reusable predicate. Called once, predicate tested per event. |
boolean | ChangeStreamContext<?> | Direct evaluation. Called on every event. |
Basic Example
Startup Validation (Fail-Fast)
FlowWarden validates@Filter compatibility at application startup and rejects invalid
configurations immediately with a clear error message. The application will fail to start
if @Filter is combined with handlers that cover operations where MongoDB does not provide
a fullDocument.
Why?
@Filter predicates typically call ctx.getFullDocument(), which returns null for
DELETE, DROP, and INVALIDATE operations. Running the filter on these events would be
error-prone at runtime — so FlowWarden rejects the combination at startup instead.
Rules
The following combinations are rejected: 1.@Filter + typed handler for a no-fullDocument operation
@Filter + @OnChange without operationTypes restriction
@Filter + @OnChange with operationTypes that includes DELETE, DROP, or INVALIDATE
Valid Combinations
The following combinations are accepted:Quick Reference
| Handler combination | @Filter allowed? |
|---|---|
@OnInsert | Yes |
@OnUpdate | Yes |
@OnReplace | Yes |
@OnInsert + @OnUpdate | Yes |
@OnDelete | No |
@OnChange (no restriction) | No |
@OnChange(operationTypes = {INSERT, UPDATE}) | Yes |
@OnChange(operationTypes = {INSERT, DELETE}) | No |
@OnInsert + @OnDelete | No |
Checkpoint Interaction
Events rejected by@Filter are not forwarded to the handler, but their resume token is
still checkpointed as lastSeenToken. This prevents reprocessing filtered events after a restart.
When @Filter is used alone (without @Pipeline), there is no gap between lastSeenToken and
lastProcessedToken — the checkpoint behavior is neutral.
Combining with @Pipeline
@Filter can coexist with @Pipeline on the same @ChangeStream,
forming a double filtering funnel:
Typical use case: pre-filter operationType = insert | update and status = PAID
server-side with @Pipeline, then verify application-side with @Filter that the tenant
is active via a Spring service call.
When combining
@Pipeline + @Filter, setting dualCheckpoint = true on
@Checkpoint is recommended to keep lastSeenToken advancing
independently.See Also
@Pipeline
Server-side aggregation pipeline filtering
Filtering Events Guide
Complete guide combining @Pipeline and @Filter
@Checkpoint
Resume token persistence and dual checkpoint
Event Handlers
@OnInsert, @OnUpdate, @OnDelete, @OnChange

