Mongoose populates virtual with list and limit
I am trying to write a mongoose query that retrieves a group of assets along with the most recent transactions for those assets. Operations are in a separate collection from assets.
To do this, I first created a virtual array in the asset model to associate assets with transactions.
schema.virtual('transactions', {
ref: 'transaction',
localField: '_id',
foreignField: '_asset',
justOne: false
})
I then ask to use .populate in the node.js express controller (note that the hardcoded "limit: 1" will become N at some point):
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);
});
}
With three assets in my test system and many transactions for each, it returns old transactions for the first two assets and nothing for the third.
When I remove "limit: 1" and return all transactions, it sorts correctly and returns transactions for all three assets.
I believe I am running into this error:
https://github.com/Automattic/mongoose/issues/4321
Any ideas for an elegant workaround?
Bouncing back all transactions will not be a viable long term solution.
source to share
This is a really kinky question. The basis here is that .populate()
and of course it is not "filled with virtual", it is not designed to work as you would expect here.
Explanation of the problem
Essentially .populate()
this is essentially another query issued by MongoDB to retrieve related data. To do this, it basically issues a query c $in
containing all the sibling field values ββfor the target that will match.
At the heart of "issue 4321" is that with parameters like "sort"
and "limit"
, the actual request that should be given such an $in
argument is actually a statement .aggregate()
that can "get the last n
results grouped for each key". This is actually not what Mongoose is currently throwing MongoDB, and it is really impractical to group items n
by available operations at this time.
You can work around this by using .aggregate()
manually, as shown at the end of the provided list, but of course only in a practically 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
}}
])
This is not a great or very effective solution, but better than other similar alternatives. I believe it's even better.
The best decision
For your case here, and I would suspect many similar cases, you do not want to include a "complete" list of transactions in parent documents, even as references, due to the potential size of the arrays being created. This type of "anti-pattern" is usually intended to avoid "virtual padding" and even $lookup
.
In your specific use case, "get last transaction", although neither is a viable solution. Because both essentially require looking at "all" transactions and then fetching from them n
.
So the "recent" or indeed "most recent" cases here actually fall back on "embedding" (at least of the links) in a "limited number" to provide a workable solution. So the clause should do just that and keep the list of transactions "recent"
inside the parent itself. This gives you a document in an included script that looks like this:
{
"_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" related transactions, but only the "most recent" ones. The point is to keep only a "small" list that suits the purpose.
So you can query "parent" directly and just $slice
from the array contained for the elements "recent"
. In the listing, I do it like:
Master.find().select({ 'recent': { '$slice': 1 } })
This returns the "last" entry from the array without any other requests to the server. This is "last" in this case, because instead, we "add" elements to this array at the same time we write to the collection "transactions"
that 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 is $push
for an array in the parent, which is modified with $position
"preprend" at the beginning of the array, so the "first" element is always the "last" transaction to be added to the parent.
Then a modifier is used here $slice
to keep the array "recent"
up to the limit of only elements n
. Thus, the "oldest" items will be "discarded" in the list when new items are added.
Then there is another practical goal, which is that by listing "master" and "transaction" in the paging script, the first query can directly use the array "recent"
. Then an additional query for new "pages" can simply filter the elements contained in the array "recent"
through $nin
and use the normal .skip()
and .limit()
or alternate "range" to get each "page" of results.
To fully demonstrate all concepts, see the list and all generated results below.
Demo 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();
}
);
Demo 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"
}
]
}
]
source to share