'How do I use Spring Mongo to group two fields and get a array of one fields based on the other?

Let me give a example here:

Two entries in the collection Author:

{
"name" : "Joe"
"Book" : "A"
},
{
"name" : "Joe"
"Book" : "B"
}

Now, if I use the aggregation function in Mongo via spring mongo, basically just to grab the books with name Joe, it could be coded like:

Aggregation agg = newAggregation(Map.class, group("name", "Book"));

AggregationResults<Map> results = mongoTemplate.aggregate(agg, "Author",
                Map.class);

Obviously I could get two Maps this way, one has entry {"name":"Joe", "Book": A}, the other has {"name" : "Joe", "Book" : "B"}

But what if I want get ONLY one result back, with one entry :

{"name" : Joe, "Books" : ["A", "B"]}?

I'm not sure if it is reachable just using one query. It certainly could be achieved by multiple steps, which I'd hate to do..



Solution 1:[1]

You need to use the $addToSet operator in your $group pipeline. This will return an array of all unique values ["A", "B"] that results from applying the $group expression to each document in a group of documents that share the same group by key "name". So in mongo shell you have

db.author.aggregate([
    { $group: {
        _id: '$name',
        Books: { $addToSet: '$Book' }
    } }
]);

which brings back the desired result

{
    "result" : [ 
        {
            "_id" : "Joe",
            "Books" : [ "B", "A" ]
        }
    ],
    "ok" : 1
}

The equivalent Spring aggregation:

Aggregation agg = newAggregation(Map.class, group("name").addToSet("Book").as("Books"));

AggregationResults<Map> results = mongoTemplate.aggregate(agg, "Author", Map.class);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1