IronWaffleMan
IronWaffleMan

Reputation: 2692

How to group/aggregate using mongoose?

I have a db full of data objects similar to books. This applies to book editions too, so if Book A has been printed 9 times, there are 9 examples of it in my database. What links these 9 examples is they have one id, say book_id. In my search results, I want to discard all results with the same book_id except one, the one with the most recent published_date. Thing is, I'm not sure how to get the aggregation to work properly. Here's what I have now:

return Book.aggregate([
    { $match: generateMLabQuery(rawQuery) },
    { $group: { _id: '$book_id' } },

The first match is simply doing normal matching things, like searching by author or genre or whatever. I was under the impression that $group would then compress all the results by unique book_id, but it's not the case, since what I get returned is simply an array of items that look like { _id: earg684rthae68g486e }. How do I get it to return the full Book document, but with all old releases discarded?

Upvotes: 0

Views: 71

Answers (2)

Elvis
Elvis

Reputation: 1143

Since the link between these books is 'book_id', it's clear that you need to group by 'book_id' field, that means for every 'book_id', you'll get an array of books.

return Book.aggregate([
    { $match: generateMLabQuery(rawQuery) },
    {
        $group: {
            _id: "$book_id",
            books: {
                $push: "$$ROOT"
            }
        }
    }
])

The above will give you an array of documents for each book_id in the books field.

But you only need one book for each book_id and that needs to be the one with the latest 'published_date', so if you sort the results on the basis of 'published_date' in descending order, you can just get the first object of the array everytime.

return Book.aggregate([
    { $match: generateMLabQuery(rawQuery) },
    {
        $sort: {
            "published_date": -1
        }
    },
    {
        $group: {
            _id: "$book_id",
            books: {
                $first: "$$ROOT"
            }
        }
    }
])

Upvotes: 1

wscourge
wscourge

Reputation: 11291

Group creates new objects; you can manipulate $first / $last accumulator operators to digest fields you need, and then $project them:

return Book.aggregate([
  { $match: generateMLabQuery(rawQuery) },
  { 
    $group: {
      _id: '$book_id',
      published_date: { $last: '$published_date' },
      title: { $first: '$title' },
      author: { $first: '$author' }
    }
  },
  {
    $project: {
      _id: '$id',
      book_id: '$_id',
      published_date: 1,
      title: 1,
      author: 1
    }
  }
])

Upvotes: 0

Related Questions