79

Is there an easy way to "$push" all fields of a document? For example:

Say I have a Mongo collection of books:

{author: "tolstoy", title:"war & peace", price:100, pages:800}
{author: "tolstoy", title:"Ivan Ilyich", price:50,  pages:100}

I'd like to group them by author - for each author, list his entire book objects:

{ author: "tolstoy",
  books: [
     {author: "tolstoy", title:"war & peace", price:100, pages:800}
     {author: "tolstoy", title:"Ivan Ilyich", price:50,  pages:100}
  ]
}

I can achieve this by explicitly pushing all fields:

{$group: {
     _id: "$author",
     books:{$push: {author:"$author", title:"$title", price:"$price", pages:"$pages"}},
}}

But is there any shortcut, something in the lines of:

// Fictional syntax...
{$group: {
    _id: "$author",
    books:{$push: "$.*"},
}}
A-Sharabiani
  • 17,750
  • 17
  • 113
  • 128
Pelit Mamani
  • 2,321
  • 2
  • 13
  • 11

3 Answers3

177

You can use $$ROOT

{ $group : {
            _id : "$author",
            books: { $push : "$$ROOT" }
        }}

Found here: how to use mongodb aggregate and retrieve entire documents

Community
  • 1
  • 1
Jurjen Ladenius
  • 1,945
  • 1
  • 12
  • 9
11

Actually you cant achieve what you are saying at all, you need $unwind

db.collection.aggregate([
    {$unwind: "$books"},

    {$group: {
         _id: "$author",
         books:{$push: {
             author:"$books.author",
             title:"$books.title",
             price:"$books.price",
             pages:"$books.pages"
         }},
    }}
])

That is how you deal with arrays in aggregation.

And what you are looking for to shortcut typing all of the fields does not exist, yet.

But specifically because of what you have to do then you could not do that anyway as you are in a way, reshaping the document.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
  • Thanks very much for replying, but my query works (tested on mongo 2.4.9). Maybe my description wasn't clear - apologies if thats' the case. I start off with a flat Mongo collection of books (totally "un-wound") and I'd like to *group* them into the mentioned format of auther+his list of books. – Pelit Mamani Mar 03 '14 at 16:00
2

If problem is that you don't want to explicitly write all fields (if your document have many fields and you need all of them in result), you could also try to do it with Map-Reduce:

db.books.mapReduce(
    function () { emit(this.author, this); },
    function (key, values) { return { books: values }; },
    { 
        out: { inline: 1 },
        finalize: function (key, reducedVal) { return reducedVal.books; } 
    }
) 
Ivan.Srb
  • 1,851
  • 1
  • 14
  • 10
  • 1
    Nice, thank :) IMHO it's a shame such a "this" isn't supported directly in $push, for 'pipeline' syntax. But thanks very much for this interesting alternative. – Pelit Mamani Mar 04 '14 at 05:48