Home > Net >  java MongoTemplate aggregate with project nested document
java MongoTemplate aggregate with project nested document

Time:03-14

I have a document.

{
"errors" : [
    {
        "priority" : 3,
        "category" : "aaa"
        "other":"nothing"
    },
    {
        "priority" : 4,
        "category" : "bbb"
        "other":"nothing"
    },
    {
        "priority" : 2,
        "category" : "ccc"
        "other":"nothing"
    },
    {
        "priority" : 3,
        "category" : "ddd"
        "other":"nothing"
    },
    {
        "priority" : 2,
        "category" : "eee"
        "other":"nothing"
    }
    ],
"file_name" : "xxx.json",
"vehicle_id" : "esdf",
"day" : "2022-03-08"
}

I execute a command with js client.

db.wty_test.aggregate({
    $project: {
        '_id': 0, 'errors.priority': 1, 'errors.category': 1, 'file_name': 1, 'vehicle_id': 1,
    }
})

I get the result I want.enter image description here The errors is an array containing objects.

Now I need to overwrite this command with java client(springboot-data-mongo). This is my java code.

import org.springframework.data.mongodb.core.MongoTemplate;
...
Aggregation aggregation = Aggregation.newAggregation(Aggregation.project("errors.priority", "errors.category", "file_name", "vehicle_id"));
mongoTemplate.aggregate(aggregation, "wty_test", HashMap.class).getMappedResults();

enter image description here The priority and category is not in errors.

How to use java to get the same result as js?

I try the nested.But it's not what I want.

enter image description here


enter image description here

CodePudding user response:

You want to be using the nested() function, like so:

AggregationOperation project = Aggregation.project("file_name", "vehicle_id").
    and("errors").nested(Fields.fields("priority","category"))

Aggregation aggregation = Aggregation.newAggregation(project);

CodePudding user response:

Here is a way to get the desired result.

Document projectn = new Document("$project", 
                        new Document("_id", 0L)
                        .append("file_name", 1L)
                        .append("vehicle_id", 1L)
                        .append("errors", 
                            new Document("$map", 
                                new Document("input", "$errors")
                                .append("in", 
                                    new Document("priority", "$$this.priority")
                                    .append("category", "$$this.category")
                                )
                            )
                        )
                    );

List<Document> pipeline = Arrays.asList(projectn);
List<Document> results = mongoOps.getCollection("collection_name")
                                 .aggregate(pipeline)
                                 .into(new ArrayList<>());

Note that this uses MongoDB Java Driver API query syntax, and Document is org.bson.Document. The conversion of the native query to Java Driver uses $map aggregation array operator (and it looks like thats the (maybe only) way).

With MongoTemplate.aggregate the code is:

    Aggregation agg = newAggregation(
            project() 
            .and( 
                VariableOperators.Map.itemsOf("errors")
                    .as("e")
                    .andApply(ctx -> new Document("priority", "$$e.priority").append("category", "$$e.category") ))
            .as("errors")
            .andExclude("_id")
            .andInclude("file_name", "vehicle_id")
    );

    AggregationResults<Document> results = mongoOps.aggregate(agg, "collection_name", Document.class);


Alternate Method:

In case your query is just about the projection, you can use the following query using MongoTemplate#find method. This is much simpler to construct and understand:

db.collection.find(
    {}, // your query filter
    { _id: 0, 'errors.category': 1, 'errors.priority': 1, file_name: 1, vehicle_id: 1 }
)

The MongoTemplate version of it:

Query query = new Query();
query.fields()
     .include("errors.priority", "errors.category", "file_name", "vehicle_id")
     .exclude("_id");
List<Document> results = mongoOps.find(query, Document.class, "collection_name");
  • Related