添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

With 4.0.1, specifically #4240 , it's not possible to use Spring AggregationExpression in $map of $project stage. A org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for CodecCacheKey{clazz=class org.springframework.data.mongodb.core.aggregation.ConditionalOperators$IfNull is thrown. It is not specific to IfNull tho.

What I want to achieve:

document in mongodb:

"_id" : ObjectId("..."), "attributes" : [ "text" : { "de" : " DE " , "en" : " EN " "text" : { "en" : " EN 2 "

Desired result

"_id" : ObjectId("..."), "attributes" : [ "text" : " DE " "text" : " EN 2 "

with following aggregation:

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);