0

I have a contacts collection with documents similar to this:

{ Name: "John Doe", BirthDate: ISODate("1980-03-12T00:00:00.000Z"), Address: "..." }

I need to get all documents, sorted by the month part of the BirthDate field. The result must include all fields of the document. The idea is to show the user which contacts have a birthday on each month.

I've tried to construct an aggregation, but can't quite figure out how to sort the BirthDate field using the $month operator.

Maybe I can use $group, but how do I make it show all fields of the document?

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
Yev
  • 276
  • 3
  • 13

1 Answers1

2

Well there is a trick to that, and depending on what you want to do you can take a couple of approaches. The most simple for is a matter of using $project in the aggregation pipeline.

I'm working with a sub-set of what knowledge I have of your fields but the general concept is to define all of them in the $project phase, plus the additional field:

db.collection.aggregate([

    // You probably want to do some matching first

    // Project your altered document form with a *new* field for "month"
    {"$project": {
        "Name": 1,
        "BirthDate": 1,
        "Address": 1,
        "month": { "$month": "$BirthDate" }               // Extra field for "month"
    }},

    // Sort by your month value
    {"$sort": { "month": 1 }},

    // Then just clean the extra part from your projection
    // ( if you need to )
    {"$project": {
        "Name": 1,
        "BirthDate": 1,
        "Address": 1,
    }},

])

If you need to do something more complex, that is going to involve some form of grouping or other operations, then save the document in the _id as long as you are doing things like grouping on the whole document:

db.collection.aggregate([

   // Save your document in the `_id` field
    {"$project": {
        _id: {
            _id: "$_id",
            "Name": 1,
            "BirthDate": 1,
            "Address": 1
        }
    }},

There was some more example usage of this posted here:

How to get back the Original document back after aggregation

Community
  • 1
  • 1
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
  • I thought going that way, and your example is great. But, the fact that I need to project all of the fields manually is bothering me. What if later I add more fields to the documents, or remove some, I will have to find all aggregations that do projection on that collection and fix them. I hope to find a way to have all of the fields included automatically. – Yev Feb 27 '14 at 13:37
  • 1
    @Yev Anoying yes. From 2.6 there will be a `$$ROOT` variable that is an alias for the **whole** document in a given pipeline phase. At the moment there is no other way around this. – Neil Lunn Feb 27 '14 at 13:39
  • @Yev As a principle, I generally don't code these statements, (except when playing / devising ), but thet get **generated** in real world code. So that is how **I** deal with what you are describing. – Neil Lunn Feb 27 '14 at 13:41
  • Ok, that's good to know. I'll do it manually then, and wait for 2.6. Can you please post a link to the $$ROOT documentation? I can't find it on MongoDB's site. – Yev Feb 27 '14 at 13:46
  • @Yev I'll bump someone on that. If you look at the documentation and change the version to **2.6** (which you can do) you can look through other features. And there is RC1 of 2.6 out now if you are game. Here's the [release notes](http://docs.mongodb.org/master/release-notes/2.6/), but still no mention of these *hidden* vars there. – Neil Lunn Feb 27 '14 at 13:50
  • Thanks, I've seen that doc. I'd be game, but need it in production pretty soon, no RC for me :( I'm not sure though whether they're releasing 2.6 in March or will go straight to 2.7 later. – Yev Feb 27 '14 at 13:54
  • 1
    @Yev 2.6 is the release. Dev branch was 2.5.x. It will be ready when it's ready. [JIRA issue](https://jira.mongodb.org/browse/SERVER-5916) for $$ROOT – Neil Lunn Feb 27 '14 at 13:57