db.collection.aggregate([
"$project": {
"_id": 1,
"attributes": {
"$map": {
"input": "$attributes",
"as": "attribute",
"in": {
"text": {
"$ifNull": [
"$$attribute.text.de",
"$$attribute.text.en"
With 4.0.0:
var attributeProjection = VariableOperators.Map
.itemsOf("attributes")
.as("attribute")
.andApply(ctx -> new Document("text",
ifNull("$$attribute.text.de").thenValueOf("$$attribute.text.en")));
var aggregation = new TypedAggregation<>(Entity.class,
project("_id")
.and(attributeProjection).as("attributes")
List<EntityProjection> results = mongoTemplate.aggregate(aggregation, EntityProjection.class)
.getMappedResults();
With 4.0.1:
var aggregation = new TypedAggregation<>(Entity.class,
project("_id")
.and(ctx -> new Document("$map",
new Document("input", "$attributes")
.append("as", "attribute")
.append("in", new Document("text",
new Document("$ifNull", new ArrayList<>(
List.of("$$attribute.text.de", "$$attribute.text.en")))
.as("attributes")
List<EntityProjection> results = mongoTemplate.aggregate(aggregation, EntityProjection.class)
.getMappedResults();
I attached an example project to reproduce the scenario. With Spring Boot 3.0.1, both tests will pass, with >3.0.1 one will fail.
my analysis
With 4.0.1 - because of #4240 - the pipeline is not mapped anymore in AggregationUtil::createPipeline
.
While I still can achieve my desired outcome, I still wanted to bring this up as it feels like a breaking change. But maybe what I did until 4.0.0 was not the "right" way to begin with? In that case I'd be happy for pointers how I should solve my problem with Spring Data MongoDB.
spring-data-mongodb-agg-project-map.zip
Thank you for the detailed report.
The change fixed an undesired behaviour that made things work for you by accident. I've to admit that the documentation of AggregationExpression
is not very clear in that regard, and I'll take steps to update it.
However you may still use ConditionalOperators
. Only make sure to call toDocument(ctx)
to trigger the mapping as outlined below.
.andApply(ctx -> new Document("text",
ifNull("$$attribute.text.de")
.thenValueOf("$$attribute.text.en")
.toDocument(ctx);