2016-05-16 69 views
5

我有收集哪些文档,如:文件返回最后一个 “真” 值的每个组

{ 
    _id: ObjectId(), 
    user: ObjectId(), 
    studentName: String, 
    createdAt: Date, 
    isAbondoned: boolean 
} 

例子是:

1-

{ 
    "_id" : ObjectId("56cd2d36a489a5b875902f0e"), 
    "user" : ObjectId("56c4cafabd5f92cd78ae49d4"), 
    "studentName" : "Aman", 
    "createdAt" : ISODate("2016-02-24T04:10:30.486+0000"), 
    "isAbandoned" : true 
} 

2 -

{ 
    "_id" : ObjectId("56cd2dcda489a5b875902fcd"), 
    "user" : ObjectId("56c4cafabd5f92cd78ae49d4"), 
    "studentName" : "Aman", 
    "createdAt" : ISODate("2016-02-24T04:13:01.932+0000"), 
    "isAbandoned" : false 
} 

3-

{ 
    "_id" : ObjectId("56cee51503b7cb7b0eda9c4c"), 
    "user" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
    "studentName" : "Rajeev", 
    "createdAt" : ISODate("2016-02-25T11:27:17.281+0000"), 
    "isAbandoned" : true, 
} 

现在我想找到学生的名单,他们的“isAbandoned”是他们最后的“createdAt”文件真实。

对于上面的例子中需要的输出是:

{ 
    "user" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
    "studentName" : "Rajeev" 
} 

因为studentName“阿曼”最大(createdAt)是第二文档和“isAbandoned”是该假。

回答

3

执行此操作的最佳方法是使用聚合框架。您需要通过“用户”$group您的文档,并使用$last累加器操作员返回每个用户的最后一个文档,但为此,需要使用$sort汇总管线操作员进行初步分类阶段。要对文档进行排序,您需要同时考虑“createdAt”字段和“用户”字段。

流水线中的最后一个阶段是$match阶段,您只选择“isAbandoned”等于true的最后一个文档。

db.students.aggregate([ 
    { "$sort": { "user": 1, "createdAt": 1 } }, 
    { "$group": { 
     "_id": "$user", 
     "last": { "$last": "$$ROOT" } 
    }}, 
    { "$match": { "last.isAbandoned": true } } 
]) 

返回是这样的:

{ 
    "_id" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
    "last" : { 
     "_id" : ObjectId("56cee51503b7cb7b0eda9c4c"), 
     "user" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
     "studentName" : "Rajeev", 
     "createdAt" : ISODate("2016-02-25T11:27:17.281Z"), 
     "isAbandoned" : true 
    } 
} 

要获得期望的结果,我们需要使用$replaceRoot管道运营商从优化版本3.4开始,以促进嵌入文档到顶级

{ 
    $replaceRoot: { newRoot: "$last" } 
} 

在旧版本中,您需要使用$project聚合流水线操作来重塑我们的文档。因此,如果我们扩展我们的管道具有以下阶段:

{ 
    "$project": { 
     "_id": "$last._id", 
     "user": "$last.user", 
     "studentName": "$last.studentName", 
     "createdAt": "$last.createdAt", 
     "isAbandoned": "$last.isAbandoned" 
}} 

它产生预期的输出:

{ 
    "_id" : ObjectId("56cee51503b7cb7b0eda9c4c"), 
    "user" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
    "studentName" : "Rajeev", 
    "createdAt" : ISODate("2016-02-25T11:27:17.281Z"), 
    "isAbandoned" : true 
} 
1

这是一个很好的例子,需要按特定字段(createdAt)对数据进行分组,然后比较结果集匹配条件。

  1. 发现最大的学生证,
  2. 比赛只有最大项条目= createdAt
  3. 检查它们是否合格的标准
  4. 重塑文件

代码

db.student.aggregate([{ 
    $group : { 
     _id : "$user", 
     created : { 
      $max : "$createdAt" 
     }, 
     documents : { 
      $push : "$$ROOT" 
     } 
    } 
}, { 
$project : { 
    _id : 0, 
    documents : { 
     $filter : { 
     input : "$documents", 
     as : "item", 
      cond : { 
       $eq : ["$$item.createdAt", "$created"] 
      } 
     } 
    }} 
}, { 
$match : { 
    "documents.isAbandoned" : true 
}}, 
{ $unwind : "$documents" }, 
{ 
$project : { 
    _id : "$documents._id", 
    user : "$documents.user", 
    studentName : "$documents.studentName", 
    createdAt : "$documents.createdAt", 
    isAbandoned : "$documents.isAbandoned", 
}} 
]) 
+0

这工作,但确实非常没有效率的,你真的不需要去通过所有这个麻烦。 – styvane

+0

如果OP使用的是旧的MongoDB版本,因为'$ filter'是3.2版中的新版本 – styvane

+1

@ user3100115 - db版本没有指定:-),所以你的代码将失败,所以我的假设,btw很好的清洁解决方案! – profesor79