使用inc mongo update更新数组元素

时间:2014-12-16 05:20:50

标签: mongodb updates mongodb-query aggregation-framework

HI我在mongo中拥有此数据,

{"articleId" : [
            {
                    "articleId" : "9514666",
                    "articleCount" : 1
            }
    ],

    "count" : NumberLong(1),
    "timeStamp" : NumberLong("1416634200000"),
    "interval" : 1,
    "tags" : "famous"
 }

我想使用这个新数据

更新它
 {"articleId" : [      
            {
                    "articleId" : "9514666",
                    "articleCount" : 3
            }
            {
                    "articleId" : "9514667",
                    "articleCount" : 3
            }
    ],

    "count" : NumberLong(6),
    "timeStamp" : NumberLong("1416634200000"),
    "interval" : 1,
    "tags" : "famous"
 }

输出中我需要的是

{"articleId" : [
            {
                    "articleId" : "9514666",
                    "articleCount" : 4
            }
            {
                    "articleId" : "9514667",
                    "articleCount" : 3
            }
    ],

    "count" : NumberLong(7),
    "timeStamp" : NumberLong("1416634200000"),
    "interval" : 1,
    "tags" : "famous"
 }

您能否建议我如何使用更新操作实现此目的 我的更新查询将标记字段作为查询参数。

1 个答案:

答案 0 :(得分:0)

你永远不会在单个查询操作中得到这个,因为目前MongoDB更新无法引用现有的字段值。当然例外的是$inc之类的运算符,但这比实际可以处理的更多。

您需要多次更新,但需要遵循一致的模型,Bulk Operations API至少可以帮助您在单个请求中发送所有这些更新:

var updoc = { 
    "articleId" : [      
        {
                "articleId" : "9514666",
                "articleCount" : 3
        },
        {
                "articleId" : "9514667",
                "articleCount" : 3
        }
    ],
    "count" : NumberLong(6),
    "timeStamp" : NumberLong("1416634200000"),
    "interval" : 1,
    "tags" : "famous"
};


var bulk = db.collection.initializeOrderedBulkOp();

// Inspect the document variable for update

// For each array entry
updoc.articleId.forEach(function(doc) {
    // First try to match the document and array entry to update
    bulk.find({
        "tags": updoc.tags,
        "articleId.articleId": doc.articleId
    }).update({
        "$inc": { "articleId.$.articleCount": doc.articleCount }
    });

    // Then try to "push" the array entry where it does not exist
    bulk.find({
        "tags": updoc.tags,
        "articleId.articleId": { "$ne": doc.articleId }
    }).update({
        "$push": { "articleId": doc }
    });
})

// Finally increment the overall count
bulk.find({ "tags": updoc.tags }).update({
    "$inc": { "count": updoc.count }
});

bulk.execute();

现在这不是“真正的”原子,并且在没有所有修改的情况下读取修改后的文档的可能性非常小。并且Bulk API将这些发送到服务器以立即处理所有内容,然后这比客户端和服务器之间的单个操作要好得多,在这些操作中,以不一致状态读取文档的可能性会高得多。 / p>

因此,对于文档中的每个数组成员来说“合并”,你都想尝试$inc所在的地方 成员在查询中匹配,并与$push匹配的新成员匹配。最后,您只需再次$inc获取合并文档与现有文档的总计数。

对于此示例,总共有5个更新操作,但都在一个包中发送。请注意,虽然响应将确认由于指定的条件,此处仅作为2个操作应用的3个操作实际上不会与文档匹配:

BulkWriteResult({
    "writeErrors" : [ ],
    "writeConcernErrors" : [ ],
    "nInserted" : 0,
    "nUpserted" : 0,
    "nMatched" : 3,
    "nModified" : 3,
    "nRemoved" : 0,
    "upserted" : [ ]
})

这是处理它的一种方法。另一种可能是单独提交每个文档,然后使用聚合框架定期将数据“合并”到分组文档中。这取决于你想要“实时”的方式。以上内容与您通常可以获得的“实时”更新一样接近。


延迟处理

如上所述,还有另一种方法,您可以考虑对此“合并”的“延迟”处理,您不需要实时更新数据。该方法考虑使用aggregation framework来执行“合并”,您甚至可以使用聚合作为数据的一般查询,但您可能希望在集合中累积。

聚合的基本前提是您将每个“更改”文档存储为集合中的单独文档,而不是实时合并。因此集合中的两个文档将表示如下:

{
    "_id" : ObjectId("548fe1c78ad2c25d4c952eee"),
    "articleId" : [
            {
                    "articleId" : "9514666",
                    "articleCount" : 1
            }
    ],
    "count" : NumberLong(1),
    "timeStamp" : NumberLong("1416634200000"),
    "interval" : 1,
    "tags" : "famous"
},
{
    "_id" : ObjectId("548fe2286032bac607405eb3"),
    "articleId" : [
            {
                    "articleId" : "9514666",
                    "articleCount" : 3
            },
            {
                    "articleId" : "9514667",
                    "articleCount" : 3
            }
    ],
    "count" : NumberLong(6),
    "timeStamp" : NumberLong("1416634200000"),
    "interval" : 1,
    "tags" : "famous"
}

为了将这些结果“合并”给定的“标签”值,您需要一个像这样的聚合管道:

db.collection.aggregate([
    // Unwinds the array members to de-normalize
    { "$unwind": "$articleId" },

    // Group the elements by "tags" value and "articleId"
    { "$group": {
        "_id": {
            "tags": "$tags",
            "articleId": "$articleId.articleId",
        },
        "articleCount": { "$sum": "$articleId.articleCount" },
        "timeStamp": { "$max": "$timeStamp" },
        "interval": { "$max": "$interval" },
    }},

    // Now group again creating the array of "merged" items
    { "$group": {
        "_id": "$tags",
        "articleId": { 
            "$push": { 
                "articleId": "$_id.articleId",
                "articleCount": "$articleCount"
            }
        },
        "count": { "$sum": "$articleCount" },
        "timeStamp": { "$max": "$timeStamp" },
        "interval": { "$max": "$interval" },
    }}
])

因此,使用“tags”和“articleId”(内部值)将结果组合在一起,获取“articleCount”字段的$sum,其中两个字段都相同,{{3}其余字段的值,这是有道理的。

在第二个$max传递中,您只需将结果文档分解为“标记”,将其下的每个匹配的“articleId”值推送到数组中。为避免重复,文档“count”在此阶段求和,其他值仅取自相同的分组。

结果是相同的“合并”文档,您可以使用上述聚合查询简单地从此类集合返回结果,或者使用这些结果为合并文​​档创建新集合(请参阅$group运算符用于一个选项)或使用与第一个示例类似的过程将这些“合并”结果与现有的“合并”集合“合并”。


这样的累积数据通常是一个广泛的主题,即使是许多常见的用例。有一个参考项目,但MongoDB解决方案架构称为$out或高容量数据源。它旨在提供一个框架或至少一个处理批量输入的参考示例(为此更改文档累积)并以一系列方式聚合这些进行分析。

实际方法取决于应用程序的总体需求。诸如此类的概念在内部由HVDF等框架使用,只需要考虑您需要多少复杂性以及最适合您应用程序访问数据的方法。