首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用排序和限制填充虚拟

用排序和限制填充虚拟
EN

Stack Overflow用户
提问于 2017-07-03 03:01:38
回答 2查看 4.8K关注 0票数 5

我正在尝试编写一个猫鼬查询,该查询将检索一组资产以及这些资产的最新事务。交易与资产是独立的集合。

为此,我首先在资产模型中创建了一个虚拟数组,以将资产链接到事务。

代码语言:javascript
复制
schema.virtual('transactions', {
    ref: 'transaction',
    localField: '_id',
    foreignField: '_asset',
    justOne: false
})

然后在一个.populate快速控制器中使用node.js查询(注意硬编码的“限制: 1”在某个时候会变成N):

代码语言:javascript
复制
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);
        });
  }

在我的测试系统中有3个资产,每一个都有一堆事务,它返回的是前两个资产的旧事务,而不是第三个资产的旧事务。

当我删除“限制:1”并返回所有事务时,它正确地排序并返回所有三个资产的事务。

我相信我会遇到这样的问题:

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

有什么好的解决办法吗?

返回所有事务将不是一个可行的长期解决方案.

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-07-03 07:08:15

这确实是个卷曲的问题。这里的基本知识是,.populate() (当然不是“用虚拟人填充”)的设计并不能像您在这里所期望的那样工作。

问题解释

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

“4321号”的核心是,对于诸如"sort""limit"这样的选项,需要提供这样一个$in参数的实际查询实际上是一个.aggregate()语句,它能够“获得每个键分组的最后一个n结果”。这实际上不是猫鼬目前向MongoDB发出的问题,也不适合在这个时候根据可用的操作对n项进行分组。

您可以手动使用.aggregate(),如所提供的清单末尾所示,当然只有在有限的情况下才能做到这一点。

代码语言:javascript
复制
  // 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
    }}
  ])

这不是一个很好的解决方案,也不是一个真正有效的解决方案,但比其他类似的解决方案更好。我相信有一种更好的方法。

更好的解决办法

对于这里的情况,我怀疑有许多类似的情况,您不希望将“完整”事务列表包含在父文档中,甚至不希望将其作为引用,因为所产生的数组的潜在大小。这种“反模式”通常是“虚拟填充”以及$lookup设计所要避免的。

但是,在“获取最新事务”的特定用例中,这两种方法都不是可行的解决方案。因为这两种方法本质上都需要查看“所有”事务,然后只从它们检索n结果。

因此,这里的“最新”或“最近”案例实际上退回到“嵌入”(至少是引用)在“有限数量”上,以便提供一个可行的解决方案。因此,我们的建议就是做到这一点,并在父程序中保留一个事务的"recent"列表。这为您提供了包含如下内容的场景中的文档:

代码语言:javascript
复制
{
        "_id" : ObjectId("5959e34adf833e1451a32661"),
        "__v" : 0,
        "name" : "One",
        "recent" : [
                ObjectId("5959e34bdf833e1451a32676"),
                ObjectId("5959e34bdf833e1451a32674"),
                ObjectId("5959e34bdf833e1451a32672"),
                ObjectId("5959e34bdf833e1451a32670"),
                ObjectId("5959e34bdf833e1451a3266e")
        ]
}

这里指出,这些不是“所有”相关交易,而是“最近”的交易。重点是,只保留一个“小”的清单,以适当的目的。

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

代码语言:javascript
复制
Master.find().select({ 'recent': { '$slice': 1 } })

它从数组中返回“最新”条目,而不需要向服务器进行任何其他查询。在本例中,它是“最新的”,因为我们在写入"transactions"集合的同时,将项“预先”添加到该数组中,该集合包含所有内容:

代码语言:javascript
复制
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“,因此”第一个“项始终是要添加到父级的”最新“事务。

然后,这里使用$slice修饰符将"recent"数组保持在n项的限制范围内。因此,随着新项目的添加,“最老”项将被“推开”。

此外,这里还有另一个实际目的:在分页场景中列出“主”和“事务”时,第一个请求可以直接使用"recent"数组。然后,对新的“页面”的附加请求,可以通过$nin过滤出$nin数组中包含的项,并使用常规的.skip().limit()或交替的“范围”分页实践来检索每个“页面”的结果。

要完整地演示所有概念,请参见下面的清单和所有生成的结果。

演示清单:

代码语言:javascript
复制
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();
  }
);

演示输出

代码语言:javascript
复制
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"
      }
    ]
  }
]
票数 9
EN

Stack Overflow用户

发布于 2021-04-01 21:02:57

对于那些在2021年阅读这篇文章的人,从mongoose 5.12.3开始,可以在模型中完成以下操作:

代码语言:javascript
复制
schema.virtual('transactions', {
    ref: 'transaction',
    localField: '_id',
    foreignField: '_asset',
    justOne: false,
    options: { sort: { 'createdAt': -1}, limit: 1},
})

然后在控制器中:

代码语言:javascript
复制
exports.getList = function (req, res) {
    Model
        .find({})
        .populate({
            path: 'transactions',
        })
        .lean()
        .exec(function (err, model) {
            if (err)
                res.send(err);
            res.json(model);
        });
  }
票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44877203

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档