如何根据Array字段的长度对文档进行排序

Tan*_*iet 7 mongodb node.js mongodb-query aggregation-framework

在我的小型ExpressJS应用程序中,我有一个像这样定义的问题模型

var mongoose = require('mongoose'),
    Schema   = mongoose.Schema;

/**
 * Question Schema
 */
var Question = new Schema({
  title: {
    type: String,
    default: '',
    trim: true,
    required: 'Title cannot be blank'
  },
  content: {
    type: String,
    default: '',
    trim: true
  },
  created: {
    type: Date,
    default: Date.now
  },
  updated: {
    type: Date,
    default: Date.now
  },
  author: {
    type: Schema.ObjectId,
    ref: 'User',
    require: true
  },
  answers : [{
    type: Schema.ObjectId,
    ref: 'Answer'
  }]
});

module.exports = mongoose.model('Question', Question);
Run Code Online (Sandbox Code Playgroud)

我希望根据答案数量列出热门问题列表.我用来执行我的目的的查询

Question.find()
  .sort({'answers.length': -1})
  .limit(5)
  .exec(function(err, data) {
    if (err) return next(err);
    return res.status(200).send(data);
  });
Run Code Online (Sandbox Code Playgroud)

但我什么都没得到.你有什么解决办法?

Bla*_*ven 11

你在这里的意思是你想要根据"answers"数组的"长度"对结果进行"排序",而不是像语法所暗示的那样称为"length"的"属性".对于记录,这里的语法是不可能的,因为您的模型是"引用的",这意味着此集合的文档中数组字段中存在的唯一数据是ObjectId这些引用文档的值.

但您可以使用.aggregate()方法和$size运算符执行此操作:

Question.aggregate(
    [
        { "$project": {
            "title": 1,
            "content": 1,
            "created": 1,
            "updated": 1,
            "author": 1,
            "answers": 1,
            "length": { "$size": "$answers" }
        }},
        { "$sort": { "length": -1 } },
        { "$limit": 5 }
    ],
    function(err,results) {
        // results in here
    }
)
Run Code Online (Sandbox Code Playgroud)

聚合管道分阶段工作.首先,结果中有一个$project字段,$size用于返回指定数组的长度.

现在有一个具有"长度"的字段,您可以按照阶段进行操作,$sort并将$limit其作为自己的阶段应用于聚合管道中.

更好的方法是始终在文档中维护"answers"数组的length属性.这使得无需其他操作即可轻松排序和查询.使用$inc运算符$push$pull数组中的项目来维护这一点非常简单:

Question.findByIdAndUpdate(id,
    {
        "$push": { "answers": answerId },
        "$inc": { "answerLength": 1 } 
    },
    function(err,doc) {

    }
)
Run Code Online (Sandbox Code Playgroud)

或者在移除时反向:

Question.findByIdAndUpdate(id,
    {
        "$pull": { "answers": answerId },
        "$inc": { "answerLength": -1 } 
    },
    function(err,doc) {

    }
)
Run Code Online (Sandbox Code Playgroud)

即使您没有使用原子操作符,在您更新"长度"时也适用相同的原则.然后查询排序很简单:

Question.find().sort({ "answerLength": -1 }).limit(5).exec(function(err,result) {

});
Run Code Online (Sandbox Code Playgroud)

由于该财产已存在于文件中.

因此,要么在.aggregate()不更改数据的情况下执行此操作,要么将数据更改为包含作为属性的长度,并且查询将非常快.


nit*_*ngh 10

您还可以使用:

db.question.find().sort({"answers":-1}).limit(5).pretty();
Run Code Online (Sandbox Code Playgroud)

  • 那么数组字段默认是按照数组长度排序的吗? (2认同)