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

时间:2015-08-18 05:08:03

标签: node.js mongodb 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);

我希望根据答案编号列出热门问题列表。我用来执行我的目的的查询

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

但我什么都没得到。你有任何解决方案吗?

2 个答案:

答案 0 :(得分:11)

你在这里的意思是你想要"排序"您的结果基于"长度" "答案"数组,而不是"属性"叫"长度"正如你的语法暗示。对于记录,这里的语法是不可能的,因为您的模型被引用",这意味着此集合的文档中数组字段中存在的唯一数据是这些引用文档的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
    }
)

聚合管道分阶段运行。首先,结果中的字段有$project,您可以使用$size返回指定数组的长度。

现在有一个带有"长度"的字段,您按照$sort$limit的各个阶段进行操作,这些阶段在汇总管道中作为自己的阶段应用。

更好的方法是始终保持"答案的长度属性。文档中的数组。这使得无需其他操作即可轻松排序和查询。使用$inc运算符作为$push或数组中的$pull项来维护此操作非常简单:

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

    }
)

或者在删除时反向:

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

    }
)

即使你没有使用原子算子,那么同样的原则适用于更新"长度"当你走的时候。然后用排序查询很简单:

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

});

由于该资产已存在于文件中。

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

答案 1 :(得分:7)

您也可以使用:

db.question.find().sort({"answers":-1}).limit(5).pretty();