mongoose填充虚拟与sorting和限制

我正在尝试编写一个mongoose查询来检索一组资产以及这些资产的最近交易。 交易与资产分开收集。

为了做到这一点,我首先在资产模型中创build了一个虚拟数组,将资产链接到事务。

schema.virtual('transactions', { ref: 'transaction', localField: '_id', foreignField: '_asset', justOne: false }) 

然后我使用.populate在一个node.jsexpression式控制器中进行查询(注意硬编码的“limit:1”在某个点上将变成N):

 exports.getList = function (req, res) { Model .find({}, { __v: 0 }) .populate({ path: 'transactions', options: { sort: { 'created_at': -1}, limit: 1}, }) .lean() .exec(function (err, model) { if (err) res.send(err); res.json(model); }); } 

在我的testing系统中有3个资产,每个资产有一堆交易,它将返回前两个资产的旧交易,第三个交易没有任何交易。

当我删除“限制:1”并返回所有交易时,它将正确sorting并返回所有三个资产的交易。

我相信我遇到了这个bug:

https://github.com/Automattic/mongoose/issues/4321

任何想法在优雅的解决方法?
返回所有交易将不是一个可行的长期解决scheme。

这确实是一个curl的问题。 这里的基础是.populate() ,当然不是“填充虚拟”,并不像你期待的那样工作。

问题解释

本质上.populate()实质上是发布到MongoDB以检索相关数据的另一个查询。 为此,它基本上发出一个$in的查询,其中包含目标匹配的所有“相关字段”值。

“issue 4321”的核心是"sort""limit"这样的选项,需要提供这样一个$in参数的实际查询实际上是一个.aggregate()语句,能够“得到最后n结果为每个键分组” 。 这实际上并不是MongoDB目前发布的任何mongoose,也不是现在可用的操作对n项目进行分组。

您可以手动使用.aggregate()手动解决该问题,如所提供的列表末尾所示,但当然实际上只是在有限的情况下。

  // 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 }} ]) 

这不是很好,也不是一个真正的高性能解决scheme,但比其他类似的select更好。 我相信,尽pipe还有更好的办法。

更好的解决scheme

对于你的情况,我会怀疑许多类似的情况,你不希望在父文档中包含“完整的”事务列表,甚至作为参考,因为可能产生的数组大小。 这种“反模式”通常是“虚拟填充”,实际上是为了避免“ $lookup ”。

但是,在您的“获得最新交易”的具体用例中,这两者都不是一个可行的解决scheme。 由于两者基本上都需要查看“所有”交易,然后只从中检索n结果。

因此,这里的“最新的”甚至是“最近的”案例实际上都是以“有限的数量”来embedded(至less是引用),以便提供可行的解决scheme。 所以build议是要做到这一点,并保持父本身内的"recent"的交易清单。 这给你在包含的情况下的文档,如下所示:

 { "_id" : ObjectId("5959e34adf833e1451a32661"), "__v" : 0, "name" : "One", "recent" : [ ObjectId("5959e34bdf833e1451a32676"), ObjectId("5959e34bdf833e1451a32674"), ObjectId("5959e34bdf833e1451a32672"), ObjectId("5959e34bdf833e1451a32670"), ObjectId("5959e34bdf833e1451a3266e") ] } 

在这里注意到,这些不是“全部”的相关交易,而只是“最近”的交易。 关键是只保留一个“小”的名单适当的目的。

通过这种方式,你可以直接查询“父”,只需$slice包含"recent"项目的数组。 在清单中,我这样做:

 Master.find().select({ 'recent': { '$slice': 1 } }) 

这将从数组中返回“最新”条目,而不会向服务器发送任何其他查询。 在这种情况下,这是“最新的”,因为相反,当我们写入"transactions"集合时,我们将这些项目“预先”添加到这个数组中,

 Transaction.create({ _asset: master._id, amount: data.amount }) .then(transaction => Master.update( { _id: transaction._asset }, { "$push": { "recent": { "$each": [transaction._id], "$position": 0, "$slice": 5 } }} ) 

其中的关键元素是对父元素的数组$push进行修改,使用$position修改数组的开始处的“preprend”,所以“first”项总是与“家长。

然后在这里使用$slice修饰符来保持"recent"数组仅限于n项目的限制。 所以当最新的项目被添加时,“最老的”项目将被“推出”列表。

这里还有另外一个实际的目的,当在一个分页场景中列出“master”和“transactions”时,第一个请求可以直接使用"recent"数组。 然后额外请求新的“页面”,可以通过$nin简单地过滤"recent"数组中包含的项目,并使用常规的.skip().limit()或备用的“范围”分页练习来检索每个“页面“的结果。

要全面展示所有概念,请参阅下面的列表和所有生成的结果。

示范列表:

 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(); } ); 

示范输出

 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" } ] } ]