我正在学习Mongodb / Mongoose / Express并且遇到了一个相当复杂的查询(相对于我目前的理解水平),我不确定如何最好地接近。我有一个集合 - 保持简单,让我们称之为实体 - 使用嵌入式动作数组:
name: String
actions: [{
name: String
date: Date
}]
我想要做的是返回一个文档数组,每个文档包含最近的操作(或最近的操作到指定的日期),以及下一个操作(基于相同的日期)。
使用一个find()查询是否可以实现,或者我需要将其分解为多个查询并合并结果以生成一个结果数组?我正在寻找最有效的路线。
答案 0 :(得分:0)
如果您的“操作”被插入,“最近”是列表中的最后一个条目,除非您专门更新项目和更改日期,否则通常会出现这种情况,那么您真正想做的就是“project”数组的最后一项。这是$slice
投影操作的用途:
Model.find({},{ "actions": { "$slice": -1 } },function(err,docs) {
// contains an array with the last item
});
如果确实要“更新”数组项并更改日期,但是您想要定期查询最新项,那么您可能最好保持数组的有序性。您可以使用一些修饰符来完成此操作,例如:
Model.update(
{
"_id": ObjectId("541f7bbb699e6dd5a7caf2d6"),
},
{
"$push": { "actions": { "$each": [], "$sort": { "date": 1 } } }
},
function(err,numAffected) {
}
);
实际上更多的是技巧,您可以使用$sort
修饰符来简单地对现有数组元素进行排序,而无需添加或删除。在2.6之前的版本中,您还需要$slice
“更新”修饰符,但如果您实际上并不想要,则可以将其设置为大于预期数组元素的值限制可能的大小,但这可能是一个好主意。
不幸的是,如果您通过$set
语句进行“更新”,那么您无法在单个更新语句中进行“排序”,因为MongoDB不会同时在阵列上同时执行这两种类型的操作。但是如果你能接受它,那么这是一种保持数组有序的方法,这样第一个查询形式就可以了。
如果按日期排序数组似乎太难了,那么你实际上可以检索.aggregate()
方法的最大值。与基本查询相比,这允许更多地处理文档,但成本更高:
Model.aggregate([
// Unwind the array to de-normalize as documents
{ "$unwind": "$actions" },
// Sort the contents per document _id and inner date
{ "$sort": { "_id": 1, "actions.date": 1 } },
// Group back with the "last" element only
{ "$group": {
"_id": "$_id",
"name": { "$last": "$name" },
"actions": { "$last": "$actions" }
}}
],
function(err,docs) {
})
这将使用$unwind
运算符“拉开”数组,然后通过“date”处理下一阶段$sort
内容。在$group
管道阶段,“_ id”表示使用原始文档密钥“收集”,$last
运算符从“最后”文档(非规范化)中选择字段值分组边界。
所以你可以做各种各样的事情,但当然最好的方法是保持你的数组有序并使用基本的投影操作符来简单地获取列表中的最后一项。