14

I have two Mongoose models: one for transactions and the other one for the tags associated with them. In order to implement some reports, I need aggregate code like this:

Transaction.aggregate([
  { $unwind: '$tags' },
  {
    $group: {
      _id: '$tags',
      amount: {
        $sum: '$amount'
      }
    }
  }
])

Question

This produces output containing _id and amount. Now, I'd like to populate the other fields (e.g. name) from the model, keeping the calculated amount column. Can I do that within a simple populate?

Edit

The schemas for the models I'm describing:

var TransactionSchema = new Schema({
  description: {
    type: String,
    trim: true
  },
  amount: {
    type: Number,
    required: 'Forneça um valor',
  },
  date: {
    type: Date,
    required: 'Forneça uma data',
    default: Date.now
  },
  fromOfx: {
    type: Boolean,
    default: false
  },
  created: {
    type: Date,
    default: Date.now
  },
  correlated: {
    type: Boolean,
    default: false
  },
  tags: [{
    type: Schema.Types.ObjectId,
    ref: 'TransactionTag'
  }],
  correlates: [{
    type: Schema.Types.ObjectId,
    ref: 'Transaction'
  }],
  user: {
    type: Schema.Types.ObjectId,
    ref: 'User'
  }
});

var TransactionTagSchema = new Schema({
  name: {
    type: String,
    required: 'Forneça um nome',
    trim: true
  },
  description: {
    type: String,
    trim: true
  },
  amount: {
    type: Number
  }
});
Guilherme
  • 608
  • 1
  • 6
  • 13
  • Hi can you share sample document ? – Rohit Jain Sep 25 '15 at 19:21
  • @JohnnyHK: I don't think it's the same thing, because I have the ObjectId of the field that I want to populate, but I need to still keep the `amount` field with the calculated value. I did try that snippet, but the column simply wasn't populated. This is my first big Node project, so I pretty much suck at debugging it. Is there anything I can do to diagnose why that command didn't work? – Guilherme Sep 26 '15 at 21:37
  • @RohitJain: edited with schema ;) – Guilherme Sep 26 '15 at 21:44

1 Answers1

37

You can populate an aggregation after you fetched the data from the MongoDB. This will look something like this:

// Your aggregate query from your question
Transaction.aggregate([{
                            $unwind: '$tags'
                        }, {
                            $group: {
                                _id: '$tags',
                                amount: {
                                    $sum: '$amount'
                                }
                            }
                        }])
    .exec(function(err, transactions) {
        // Don't forget your error handling
        // The callback with your transactions
        // Assuming you are having a Tag model
        Tag.populate(transactions, {path: '_id'}, function(err, populatedTransactions) {
            // Your populated translactions are inside populatedTransactions
        });
    });
Thomas Bormans
  • 5,156
  • 6
  • 34
  • 51
  • 2
    Perfect! I was trying to pass populate with `{path: 'tags'}`, which, now that I think about it, makes me feel kinda silly. Just one more thing now, doing this throws my tags into the `_id` field. Is there a Mongo-y way to make them go to a `tags` attribute instead? – Guilherme Sep 29 '15 at 17:26
  • Ok, I just found out about [`$project`](http://docs.mongodb.org/manual/reference/operator/aggregation/project/). Great! – Guilherme Sep 29 '15 at 17:47
  • Also as of 3.2 you can now use the $lookup feature to perform joins inthe aggregation pipeline - https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/ – cyberwombat May 26 '16 at 23:04
  • @cyberwombat is correct, more detail [in this answer](https://stackoverflow.com/a/36021832/441930) to another similar question. – Mr5o1 Jan 30 '20 at 02:10