This is a curly issue indeed. The basics here is that .populate()
and certainly not "populate with virtuals", is not designed to work like you are expecting here.
Issue Explained
In essence .populate()
is essentially another query issued to MongoDB to retrieve related data. For this it basically issues a query with $in
containing all of the "related field" values for the target to match on.
What is at the core of the "issue 4321" is that with such options as "sort"
and "limit"
the actual query that needs to be provided with such an $in
argument is in fact an .aggregate()
statement that would be able to "get the last n
results grouped for each key". This is not actually what mongoose presently issues to MongoDB, nor is it really practical to group n
items by available operations at this time.
You can work around that using .aggregate()
manually, as demonstrated at the end of the provided listing, but of course only practically in a limited number of cases.
// Get latest transactions for each master
Transaction.aggregate([
{ '$match': {
'_asset': {
'$in': masters.map(m => m._id)
}
}},
{ '$sort': { '_asset': 1, 'createdAt': -1 } },
{ '$group': {
'_id': '$_asset',
'amount': { '$first': '$amount' },
'createdAt': { '$first': '$createdAt' },
'updatedAt': { '$first': '$updatedAt' },
'did': { '$first': '$_id' }
}},
{ '$project': {
'_id': '$did',
'_asset': '$_id',
'amount': 1,
'createdAt': 1,
'updatedAt': 1
}}
])
It's not great and not a really performant solution, but better than other similar alternatives. There is I believe though a still better way.
A Better Solution
For your case here, and I would suspect many similar cases, you don't want to include the "full" transactions list within the parent documents even as references due to the potential size of the arrays produced. This sort of "anti-pattern" is generally what "virtual populate" and indeed $lookup
are designed to avoid.
In your particular use case of "get the latest transaction" though, neither of those are a viable solution. Since both would essentially require looking at "all" transactions and then only retrieving n
results from them.
So the "latest" or indeed the "most recent" cases here actually falls back to "embedding" ( at least references ) at a "limited number" in order to provide a workable solution. So the proposal is to do exactly that, and keep a "recent"
list of transactions within the parent itself. This gives you a document in the scenario included that looks like:
{
"_id" : ObjectId("5959e34adf833e1451a32661"),
"__v" : 0,
"name" : "One",
"recent" : [
ObjectId("5959e34bdf833e1451a32676"),
ObjectId("5959e34bdf833e1451a32674"),
ObjectId("5959e34bdf833e1451a32672"),
ObjectId("5959e34bdf833e1451a32670"),
ObjectId("5959e34bdf833e1451a3266e")
]
}
Noting here that these are not "all" the related transactions but just the "most recent" ones. The point is to only keep a "small" list as appropriate to the purpose.
In this way, you can directly query the "parent" and just $slice
off the array contained for the "recent"
items. In the listing I do this as:
Master.find().select({ 'recent': { '$slice': 1 } })
That returns the "latest" entry from the array without any other query to the server. It's the "latest" in this case, because instead we "prepend" items to this array at the same time as we write to the "transactions"
collection, which has everything:
Transaction.create({ _asset: master._id, amount: data.amount })
.then(transaction =>
Master.update(
{ _id: transaction._asset },
{ "$push": {
"recent": {
"$each": [transaction._id],
"$position": 0,
"$slice": 5
}
}}
)
The key element there is the $push
to the array in the parent which is modified with the $position
to "preprend" at the beginning of the array so the "first" item is always the "latest" transaction to be added related to the parent.
Then the $slice
modifier is used here to keep the "recent"
array to a limit of n
items only. So the "oldest" items will be "pushed off" the list as newer items are added.
There is then in addition another practical purpose here that when listing the "master" and "transactions" in a paging scenario, the first request can use the "recent"
array directly. Then additional request for new "pages", can simply filter out the items contained within the "recent"
array via $nin
and use the regular .skip()
and .limit()
or alternate "range" paging practice to retrieve each "page" of results.
To demonstrate all concepts in full, see the listing and all the generated results below.
Demonstration listing:
const async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.set('debug',true);
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/prepend');
const transactionSchema = new Schema({
_asset: { type: Schema.Types.ObjectId, ref: 'Master' },
amount: Number
},{
timestamps: {
createdAt: 'createdAt'
}
});
const Transaction = mongoose.model('Transaction', transactionSchema);
const masterSchema = new Schema({
name: String,
recent: [{ type: Schema.Types.ObjectId, ref: 'Transaction' }]
});
masterSchema.virtual('transactions', {
ref: 'Transaction',
localField: '_id',
foreignField: '_asset',
justOne: false
});
const Master = mongoose.model('Master', masterSchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
async.series(
[
// Clean data
(callback) =>
async.each(mongoose.models,(model,callback) =>
model.remove({},callback),callback),
// Create Masters
(callback) =>
Master.insertMany(['One','Two'].map( name => ({ name })),callback),
// Add 10 transactions to each master
(callback) =>
async.each(['One','Two'],(name,callback) =>
async.eachSeries(
Array.apply(null,Array(10)).map((e,i) => ({ name, amount: i+1 })),
(data,callback) => {
Master.findOne({ name: data.name })
.then(master =>
Transaction.create({ _asset: master._id, amount: data.amount })
)
.then(transaction =>
Master.update(
{ _id: transaction._asset },
{ "$push": {
"recent": {
"$each": [transaction._id],
"$position": 0,
"$slice": 5
}
}}
)
)
.then(res => callback())
.catch(callback)
},
callback
),
callback),
// Show populated recent 1 entry only
(callback) =>
Master.find().select({ 'recent': { '$slice': 1 } })
.populate('recent').exec((err,results) => {
if (err) callback(err);
log(results);
callback();
}),
// Populate recent - page 1 then fetch next page
(callback) =>
async.waterfall(
[
(callback) =>
Master.findOne({ name: 'One' }).populate('recent')
.lean()
.exec((err,master) => {
if (err) callback(err);
log(master);
callback(null,{
_asset: master._id,
exclude: master.recent.map( r => r._id )
});
}),
(options,callback) =>
Transaction.find({
_asset: options._asset,
_id: { '$nin': options.exclude }
}).sort({ 'createdAt': -1 }).limit(5)
.exec((err,transactions) => {
if (err) callback(err);
log(transactions)
callback();
})
],
callback
),
// Issue 4321 - Fix - Manual populate with aggregate
(callback) =>
Master.find().select('-recent').exec()
.then(masters => {
// Get latest transactions for each master
Transaction.aggregate([
{ '$match': {
'_asset': {
'$in': masters.map(m => m._id)
}
}},
{ '$sort': { '_asset': 1, 'createdAt': -1 } },
{ '$group': {
'_id': '$_asset',
'amount': { '$first': '$amount' },
'createdAt': { '$first': '$createdAt' },
'updatedAt': { '$first': '$updatedAt' },
'did': { '$first': '$_id' }
}},
{ '$project': {
'_id': '$did',
'_asset': '$_id',
'amount': 1,
'createdAt': 1,
'updatedAt': 1
}}
]).exec((err,transactions) => {
// Map latest transactions to master
masters = masters.map(
m => Object.assign(
m.toObject(),
{
transactions: transactions.filter(
t => t._asset.toHexString() === m._id.toHexString()
)
}
)
);
log(masters);
callback();
})
}).catch(callback)
],
(err) => {
if (err) throw err;
mongoose.disconnect();
}
);
Demonstration Output
Mongoose: transactions.remove({}, {})
Mongoose: masters.remove({}, {})
Mongoose: masters.insertMany([ { __v: 0, name: 'One', _id: 5959e34adf833e1451a32661, recent: [] }, { __v: 0, name: 'Two', _id: 5959e34adf833e1451a32662, recent: [] } ], null)
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:14 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:14 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 1, _id: ObjectId("5959e34adf833e1451a32663"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 1, _id: ObjectId("5959e34adf833e1451a32664"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34adf833e1451a32664") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34adf833e1451a32663") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 2, _id: ObjectId("5959e34bdf833e1451a32665"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 2, _id: ObjectId("5959e34bdf833e1451a32666"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32666") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32665") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 3, _id: ObjectId("5959e34bdf833e1451a32667"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 3, _id: ObjectId("5959e34bdf833e1451a32668"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32668") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32667") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 4, _id: ObjectId("5959e34bdf833e1451a32669"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 4, _id: ObjectId("5959e34bdf833e1451a3266a"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a3266a") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32669") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 5, _id: ObjectId("5959e34bdf833e1451a3266b"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 5, _id: ObjectId("5959e34bdf833e1451a3266c"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a3266c") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a3266b") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 6, _id: ObjectId("5959e34bdf833e1451a3266d"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 6, _id: ObjectId("5959e34bdf833e1451a3266e"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a3266e") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a3266d") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 7, _id: ObjectId("5959e34bdf833e1451a3266f"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 7, _id: ObjectId("5959e34bdf833e1451a32670"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32670") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a3266f") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 8, _id: ObjectId("5959e34bdf833e1451a32671"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 8, _id: ObjectId("5959e34bdf833e1451a32672"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32672") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32671") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 9, _id: ObjectId("5959e34bdf833e1451a32673"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 9, _id: ObjectId("5959e34bdf833e1451a32674"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32674") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32673") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: masters.findOne({ name: 'Two' }, { fields: {} })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32662"), amount: 10, _id: ObjectId("5959e34bdf833e1451a32675"), __v: 0 })
Mongoose: transactions.insert({ updatedAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), createdAt: new Date("Mon, 03 Jul 2017 06:25:15 GMT"), _asset: ObjectId("5959e34adf833e1451a32661"), amount: 10, _id: ObjectId("5959e34bdf833e1451a32676"), __v: 0 })
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32661") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32676") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.update({ _id: ObjectId("5959e34adf833e1451a32662") }, { '$push': { recent: { '$each': [ ObjectId("5959e34bdf833e1451a32675") ], '$slice': 5, '$position': 0 } } }, {})
Mongoose: masters.find({}, { fields: { recent: { '$slice': 1 } } })
Mongoose: transactions.find({ _id: { '$in': [ ObjectId("5959e34bdf833e1451a32676"), ObjectId("5959e34bdf833e1451a32675") ] } }, { fields: {} })
[
{
"_id": "5959e34adf833e1451a32661",
"__v": 0,
"name": "One",
"recent": [
{
"_id": "5959e34bdf833e1451a32676",
"updatedAt": "2017-07-03T06:25:15.282Z",
"createdAt": "2017-07-03T06:25:15.282Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 10,
"__v": 0
}
]
},
{
"_id": "5959e34adf833e1451a32662",
"__v": 0,
"name": "Two",
"recent": [
{
"_id": "5959e34bdf833e1451a32675",
"updatedAt": "2017-07-03T06:25:15.280Z",
"createdAt": "2017-07-03T06:25:15.280Z",
"_asset": "5959e34adf833e1451a32662",
"amount": 10,
"__v": 0
}
]
}
]
Mongoose: masters.findOne({ name: 'One' }, { fields: {} })
Mongoose: transactions.find({ _id: { '$in': [ ObjectId("5959e34bdf833e1451a32676"), ObjectId("5959e34bdf833e1451a32674"), ObjectId("5959e34bdf833e1451a32672"), ObjectId("5959e34bdf833e1451a32670"), ObjectId("5959e34bdf833e1451a3266e") ] } }, { fields: {} })
{
"_id": "5959e34adf833e1451a32661",
"__v": 0,
"name": "One",
"recent": [
{
"_id": "5959e34bdf833e1451a32676",
"updatedAt": "2017-07-03T06:25:15.282Z",
"createdAt": "2017-07-03T06:25:15.282Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 10,
"__v": 0
},
{
"_id": "5959e34bdf833e1451a32674",
"updatedAt": "2017-07-03T06:25:15.264Z",
"createdAt": "2017-07-03T06:25:15.264Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 9,
"__v": 0
},
{
"_id": "5959e34bdf833e1451a32672",
"updatedAt": "2017-07-03T06:25:15.216Z",
"createdAt": "2017-07-03T06:25:15.216Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 8,
"__v": 0
},
{
"_id": "5959e34bdf833e1451a32670",
"updatedAt": "2017-07-03T06:25:15.195Z",
"createdAt": "2017-07-03T06:25:15.195Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 7,
"__v": 0
},
{
"_id": "5959e34bdf833e1451a3266e",
"updatedAt": "2017-07-03T06:25:15.180Z",
"createdAt": "2017-07-03T06:25:15.180Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 6,
"__v": 0
}
]
}
Mongoose: transactions.find({ _id: { '$nin': [ ObjectId("5959e34bdf833e1451a32676"), ObjectId("5959e34bdf833e1451a32674"), ObjectId("5959e34bdf833e1451a32672"), ObjectId("5959e34bdf833e1451a32670"), ObjectId("5959e34bdf833e1451a3266e") ] }, _asset: ObjectId("5959e34adf833e1451a32661") }, { sort: { createdAt: -1 }, limit: 5, fields: {} })
[
{
"_id": "5959e34bdf833e1451a3266c",
"updatedAt": "2017-07-03T06:25:15.164Z",
"createdAt": "2017-07-03T06:25:15.164Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 5,
"__v": 0
},
{
"_id": "5959e34bdf833e1451a3266a",
"updatedAt": "2017-07-03T06:25:15.135Z",
"createdAt": "2017-07-03T06:25:15.135Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 4,
"__v": 0
},
{
"_id": "5959e34bdf833e1451a32668",
"updatedAt": "2017-07-03T06:25:15.080Z",
"createdAt": "2017-07-03T06:25:15.080Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 3,
"__v": 0
},
{
"_id": "5959e34bdf833e1451a32666",
"updatedAt": "2017-07-03T06:25:15.039Z",
"createdAt": "2017-07-03T06:25:15.039Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 2,
"__v": 0
},
{
"_id": "5959e34adf833e1451a32664",
"updatedAt": "2017-07-03T06:25:15.009Z",
"createdAt": "2017-07-03T06:25:15.009Z",
"_asset": "5959e34adf833e1451a32661",
"amount": 1,
"__v": 0
}
]
Mongoose: masters.find({}, { fields: { recent: 0 } })
Mongoose: transactions.aggregate([ { '$match': { _asset: { '$in': [ 5959e34adf833e1451a32661, 5959e34adf833e1451a32662 ] } } }, { '$sort': { _asset: 1, createdAt: -1 } }, { '$group': { _id: '$_asset', amount: { '$first': '$amount' }, createdAt: { '$first': '$createdAt' }, updatedAt: { '$first': '$updatedAt' }, did: { '$first': '$_id' } } }, { '$project': { _id: '$did', _asset: '$_id', amount: 1, createdAt: 1, updatedAt: 1 } } ], {})
[
{
"_id": "5959e34adf833e1451a32661",
"__v": 0,
"name": "One",
"transactions": [
{
"amount": 10,
"createdAt": "2017-07-03T06:25:15.282Z",
"updatedAt": "2017-07-03T06:25:15.282Z",
"_id": "5959e34bdf833e1451a32676",
"_asset": "5959e34adf833e1451a32661"
}
]
},
{
"_id": "5959e34adf833e1451a32662",
"__v": 0,
"name": "Two",
"transactions": [
{
"amount": 10,
"createdAt": "2017-07-03T06:25:15.280Z",
"updatedAt": "2017-07-03T06:25:15.280Z",
"_id": "5959e34bdf833e1451a32675",
"_asset": "5959e34adf833e1451a32662"
}
]
}
]