Skip to content

Conversation

@hs-lsong
Copy link
Collaborator

Summary

This PR introduces an optimization for chained filter expressions in Jinjava templates. Instead of creating deeply nested AST nodes for expressions like {{ foo|filter1|filter2|filter3 }}, a flattened AstFilterChain node is used, which improves performance by reducing recursive call overhead.

Key changes:

  • Add AstFilterChain class that processes filters iteratively instead of recursively
  • Add FilterSpec class to represent individual filter specifications in the chain
  • Make optimization configurable via enableFilterChainOptimization in JinjavaConfig (defaults to false)
  • Note: This optimization is disabled for eager execution mode - EagerExtendedParser overrides the config to always use the traditional nested approach

Test plan

  • Added AstFilterChainPerformanceTest to verify both approaches produce identical results
  • Automated test verifies optimized version outperforms unoptimized approach
  • Existing tests pass with optimization disabled (default behavior unchanged)

hs-lsong and others added 5 commits January 12, 2026 10:31
- Add enableFilterChainOptimization config option to JinjavaConfig (defaults to false)
- Modify ExtendedParser to conditionally use AstFilterChain based on config
- Override shouldUseFilterChainOptimization() in EagerExtendedParser to always return false
- Remove EagerAstFilterChain.java as eager mode now uses old nested method approach
- Revert test files to expect old format for eager execution
- Add AstFilterChainPerformanceTest to verify optimization performance
- Tests verify both approaches produce identical results
- Includes manual benchmark test (ignored by default) for detailed comparison
- Automated test verifies optimized version is faster than unoptimized (at least 5% improvement)
- Uses nested property access (content.text) to better simulate real-world usage
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
private ExecutionMode executionMode = DefaultExecutionMode.instance();
private LegacyOverrides legacyOverrides = LegacyOverrides.NONE;
private boolean enablePreciseDivideFilter = false;
private boolean enableFilterChainOptimization = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do all the tests pass if this is set to true?

Copy link
Contributor

@jasmith-hs jasmith-hs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks cool!

Comment on lines +69 to +70
String filterKey = ExtendedParser.FILTER_PREFIX + spec.getName();
interpreter.getContext().addResolvedValue(filterKey);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be tested for parity with regular filter parsing and evaluation

Comment on lines +173 to +180
builder.append('(');
for (int i = 0; i < params.getCardinality(); i++) {
if (i > 0) {
builder.append(", ");
}
params.getChild(i).appendStructure(builder, bindings);
}
builder.append(')');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
builder.append('(');
for (int i = 0; i < params.getCardinality(); i++) {
if (i > 0) {
builder.append(", ");
}
params.getChild(i).appendStructure(builder, bindings);
}
builder.append(')');
params.appendStructure(builder, bindings);

* Run with: mvn test -Dtest=AstFilterChainPerformanceTest
* Or run the main() method directly for more detailed output.
*/
public class AstFilterChainPerformanceTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests should be made separate from performance tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants