Mongodb:子文档数组的聚合值

时间:2015-08-25 13:21:23

标签: javascript mongodb mongodb-query aggregation-framework

我收集了文件:

{
    "_id": ObjectId("55dc62647cda24224372e308"),
    "last_modified": ISODate("2015-07-01T15:57:26.874Z"),
    "services": [
        {"last_modified": ISODate("2015-05-08T07:10:11.250Z")},
        {...}
    ]
}

我需要通过查找其服务的max last_updated值来刷新文档的last_modified字段:

>db.documents.find().map(function(d){
    db.documents.update(
        {_id: d._id},
        {$set: {last_updated: Math.max(d.services.last_updated)}}
    )
})
Tue Aug 25 16:01:20.536 TypeError: Cannot read property 'last_modified' of undefined

如何在数组中访问和聚合子文档的属性?

1 个答案:

答案 0 :(得分:2)

这里的基本过程是你需要从数组中获取最大的排序日期并从那里获取值。当然,您需要一个循环,并且您无法直接在update语句中访问文档的值。所以你需要先阅读它,但Bulk操作在这里有帮助:

 db.documents.update(
     { "_id": id },
     { "$push": { 
         "services": {
             "$each": [{ "last_modified": date }],
             "$sort": { "last_modified": 1 }
     }}
)

更好的是,考虑在添加新项目时对数组本身进行排序。这基本上是使用$sort

$push修饰符完成的
$sort

甚至忘记了var bulk = db.documents.initializeOrderedBulkOp(), count = 0; db.documents.find( {}, { "last_modified": { "$slice": -1} } ).forEach(function(doc) { bulk.find({ "_id": doc._id }).updateOne({ "$set": { "last_modified": doc.last_modified[0] } }); count++; if ( count % 1000 == 0 ) { bulk.execute(); bulk = db.documents.initializeOrderedBulkOp(); } }); if ( count % 1000 != 0 ) bulk.execute(); ,因为无论如何都要将所有数组值附加到结尾,除非你告诉操作不这样做。

然后你可以使用$slice基本上缩短程序。

var bulk = db.documents.initializeOrderedBulkOp(),
    count = 0;

db.documents.aggregate([
    { "$unwind": "$services" },
    { "$group": {
        "_id": "$_id",
        "last_modified": { "$max": "$services.last_modified" }
    }}
]).forEach(function(doc) {

  bulk.find({ "_id": doc._id }).updateOne({
    "$set": { "last_modified": doc.last_modified }
  });
  count++;

  if ( count % 1000 == 0 ) {
    bulk.execute();
    bulk = db.documents.initializeOrderedBulkOp();
  }

});

if ( count % 1000 != 0 )
  bulk.execute();

聚合框架可以在这里使用,但考虑到从每个文档的对象获取最大日期值是多么简单,实际上并不是必需的。

public class ServiceBean
{
    @ColumnMapping(value = "service_name")
    private String  serviceName;
    @ColumnMapping(value = "service_desc")
    private String  serviceDesc;

    public String getServiceName()
    {
        return serviceName;
    }

    public void setServiceName(String serviceName)
    {
        this.serviceName = serviceName;
    }

    public String getServiceDesc()
    {
        return serviceDesc;
    }

    public void setServiceDesc(String serviceDesc)
    {
        this.serviceDesc = serviceDesc;
    }
}

由于$unwind的使用,这实际上带来的成本远高于必要的成本。