【问题标题】:Sorting by virtual field in mongoDB (mongoose)按 mongoDB (mongoose) 中的虚拟字段排序
【发布时间】:2012-11-07 07:34:22
【问题描述】:

假设我有一些架构,它有一个像这样的虚拟字段

var schema = new mongoose.Schema(
{
    name: { type: String }
},
{
    toObject: { virtuals: true },
    toJSON: { virtuals: true }
});

schema.virtual("name_length").get(function(){
    return this.name.length;
});

在查询中是否可以按虚拟字段对结果进行排序?类似的东西

schema.find().sort("name_length").limit(5).exec(function(docs){ ... });

当我尝试这个时,结果很简单,没有排序......

【问题讨论】:

    标签: node.js mongodb sorting mongoose


    【解决方案1】:

    您将无法按虚拟字段排序,因为它们没有存储到数据库中。

    虚拟属性是便于携带的属性 但不会持久化到 mongodb。

    http://mongoosejs.com/docs/2.7.x/docs/virtuals.html

    【讨论】:

    • 我知道什么是虚拟属性并且它们不存储在数据库中。我在徘徊,也许可能有一些插件或特殊方法可以调用虚拟属性的所有吸气剂,然后对文档进行排序。我想没有这样的事情。
    • 那么,我想我的解决方案应该是在得到结果后在javascript中进行排序和限制?
    • 我猜你不能在查找条件下使用虚拟对象?
    • 取决于你有多少记录......你基本上必须检索所有项,然后排序。如果您有数百万/想要分页结果,那将是低效的
    • 嗯,我认为可能不是数百万,而是数千个结果,我需要对它们进行排序......
    【解决方案2】:

    Schema 中定义的虚拟不会注入到生成的 MongoDB 查询中。一旦从数据库中检索到这些函数,它们就会在适当的时刻为每个文档运行。

    为了达到您想要达到的目的,您还需要在 MongoDB 查询中定义虚拟字段。例如,在聚合的 $project 阶段。

    但是,在按虚拟字段排序时需要牢记以下几点:

    • 投影的文档只存在于内存中,所以如果我们只添加一个字段,并且在排序之前将搜索结果的整个文档都放在内存中,将会带来巨大的性能成本
    • 由于上述原因,排序时根本不会使用索引

    下面是一个关于如何在保持相对良好性能的同时按虚拟字段排序的一般示例:

    假设您有一组球队,每个球队都包含直接存储在文档中的球员数组。现在,要求要求我们按照 favouredPlayer 的排名对这些球队进行排序,其中 favouredPlayer 基本上是一个虚拟属性,包含在特定条件下球队最相关的球员(在这个例子中,我们只想考虑进攻和防守球员) .此外,上述标准取决于用户的选择,因此不能持久化到文档中。

    最重要的是,我们的“团队”文档非常大,所以为了减轻内存排序对性能的影响,我们只投影我们需要排序的字段,然后在限制结果后恢复原始文档.

    查询:

    [
      // find all teams from germany
      { '$match': { country: 'de' } },
      // project only the sort-relevant fields
      // and add the virtual favoredPlayer field to each team
      { '$project': {
        rank: 1,
        'favoredPlayer': {
          '$arrayElemAt': [
            {
              // keep only players that match our criteria
              $filter: {
                input: '$players',
                as: 'p',
                cond: { $in: ['$$p.position', ['offense', 'defense']] },
              },
            },
            // take first of the filtered players since players are already sorted by relevance in our db
            0,
          ],
        },
      }},
      // sort teams by the ranking of the favoredPlayer
      { '$sort': { 'favoredPlayer.ranking': -1, rank: -1 } },
      { '$limit': 10 },
      // $lookup, $unwind, and $replaceRoot are in order to restore the original database document
      { '$lookup': { from: 'teams', localField: '_id', foreignField: '_id', as: 'subdoc' } },
      { '$unwind': { path: '$subdoc' } },
      { '$replaceRoot': { newRoot: '$subdoc' } },
    ];
    

    对于您上面给出的示例,代码可能如下所示:

    var schema = new mongoose.Schema(
      { name: { type: String } },
      {
        toObject: { virtuals: true },
        toJSON: { virtuals: true },
      });
    
    schema.virtual('name_length').get(function () {
      return this.name.length;
    });
    
    const MyModel = mongoose.model('Thing', schema);
    
    MyModel
      .aggregate()
      .project({
        'name_length': {
          '$strLenCP': '$name',
        },
      })
      .sort({ 'name_length': -1 })
      .exec(function(err, docs) {
        console.log(docs);
      });
    

    【讨论】:

      猜你喜欢
      • 2019-10-19
      • 2019-03-19
      • 2014-03-15
      • 2021-04-19
      • 2015-09-19
      • 2018-10-22
      • 2015-06-10
      • 2020-10-14
      • 2019-03-29
      相关资源
      最近更新 更多