spring-projects / spring-projects/spring-data-mongodb
Add support for building aggregation pipelines from json strings with bindable parameters
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 1.7k
- Forks
- 1.1k
- PR merge metrics
- No merged PRs in 30d
Description
I'm currently converting aggregations pipelines from MongoRepository to concrete classes with MongoTemplate, due to #4808, among other issues.
In repositories, you can write pipelines as an array of json strings with placeholders for parameter binding:
@Aggregation(pipeline = {
"""
{
$match: {
name: ?0
}
}
""",
"""
{
$count: count
}
"""
})
long countByName(String name);
This is easy enough to port to an Aggregation to use with MongoTemplate, but it can become quite burdensome for more complex pipelines.
To simplify the process, I have created a couple helper classes, shamelessly copying taking inspiration from StringAggregationOperation (which is not public) combined with BindableMongoExpression:
public class BindableAggregationOperation implements AggregationOperation {
private static final Pattern OPERATOR_PATTERN = Pattern.compile("\\$\\w+");
private final Class<?> domainType;
private final BindableMongoExpression expression;
private final String operator;
public BindableAggregationOperation(Class<?> domainType, BindableMongoExpression expression, String operator) {
this.domainType = domainType;
this.expression = expression;
this.operator = operator;
}
public static BindableAggregationOperation stage(String json, @Nullable Object... args) {
return stage(null, json, args);
}
public static BindableAggregationOperation stage(@Nullable Class<?> domainType, String json, @Nullable Object... args) {
json = json.trim(); // remove trailing whitespaces to work more easily with text blocks
BindableMongoExpression expression = new BindableMongoExpression(json, args);
Matcher matcher = OPERATOR_PATTERN.matcher(json);
String operator = matcher.find() ? matcher.group() : null;
return new BindableAggregationOperation(domainType, expression, operator);
}
public BindableAggregationOperation bind(Object... args) {
return new BindableAggregationOperation(domainType, expression.bind(args), operator);
}
@Override
public Document toDocument(@Nullable AggregationOperationContext context) {
return context.getMappedObject(expression.toDocument(), domainType);
}
@Override
public String getOperator() {
return operator != null ? operator : AggregationOperation.super.getOperator();
}
}
public class BindableAggregation {
private final List<BindableAggregationOperation> operations;
public BindableAggregation(List<BindableAggregationOperation> operations) {
this.operations = operations;
}
public static BindableAggregation newAggregation(String... stages) {
return newAggregation(null, stages);
}
public static BindableAggregation newAggregation(Class<?> domainType, String... stages) {
List<BindableAggregationOperation> operations = Stream.of(stages)
.map(stage -> BindableAggregationOperation.stage(domainType, stage))
.toList();
return new BindableAggregation(operations);
}
public Aggregation bind(Object... args) {
return Aggregation.newAggregation(operations.stream().map(op -> op.bind(args)).toList());
}
}
This can be used as follows:
Aggregation aggregation = BindableAggregation.newAggregation(
"""
{
$match: {
name: ?0
}
}
""",
"""
{
$count: count
}
""")
.bind(name);
mongoTemplate.aggregate(aggregation, InputType.class, OutputType.class);
And supports type and properties conversion as well as parameter binding.
Is this a feature the Spring Data MongoDB project would be interested in?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading StringAggregationOperation.java and BindableMongoExpression.java, then inspect the AggregationOperation and MongoTemplate APIs involved in aggregation construction. Determine whether the proposed public API should support JSON stages, bindable parameters, and type/property conversion; done means the project has an agreed and integrated approach for that feature.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, mongodb
- Domain
- databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100