如何更新MongoDB中的字段,该字段汇总子文档的值?
我有一份文件的结构如下:如何更新MongoDB中的字段,该字段汇总子文档的值?,mongodb,pymongo,Mongodb,Pymongo,我有一份文件的结构如下: { 'item_id': '12345' 'total_score': 100, 'user_scores': { 'ABC': 40, 'DEF': 60 } } 我使用的是PyMongo,但MongoDB的文档似乎很容易跨不同的发行版进行翻译。使用PyMongo,我可以通过以下方式更新用户分数: collection.update_one( { 'item_id': '12345' },
{
'item_id': '12345'
'total_score': 100,
'user_scores': {
'ABC': 40,
'DEF': 60
}
}
我使用的是PyMongo,但MongoDB的文档似乎很容易跨不同的发行版进行翻译。使用PyMongo,我可以通过以下方式更新用户分数:
collection.update_one(
{ 'item_id': '12345' },
{ '$set': { 'user_scores.GHI': 20 } },
upsert=True
)
其结果是:
{
'item_id': '12345'
'total_score': 100,
'user_scores': {
'ABC': 40,
'DEF': 60,
'GHI': 20
}
}
问题当然是总分现在不正确。我想更新总分,以便在将来的查询中,我可以快速确定每个结果的分数,甚至可以按分数排序
一种解决方案是使用find_One({'item_id:'12345'})
(如果不存在则创建),然后使用新分数更新现有文档,并更新总分。这里的问题是,我想同时运行数千个这样的程序,对一系列请求调用bulk\u write
效率要高得多
因此,更好的解决方案是执行两个顺序更新请求:
request1 = UpdateOne(
{ 'item_id' : '12345' },
{ '$set': { 'user_scores.GHI': 20 } },
upsert = True
)
request2 = UpdateOne(
{ 'item_id' : '12345' },
{ '$set': { 'total_score': { '$sum': { '$values': 'user_scores' } } } },
upsert = True
)
第一个请求更新用户分数,与之前相同。第二个要求,有两个概念。这句话的语法不正确,但我想做的是:
我需要从用户评分
字典中获取值<代码>{'$values':'user_scores'}
就是我试图传达这一点的方式
{'$sum':{'$values':'user_scores'}
表示我可以连续运行这些批处理更新,因此不会出现求和错误的风险。拥有
总分
字段的危险在于该字段始终未更新,因此不包含正确的数字。我认为这是基于文档的模型的常见情况?如果您使用的是Mongo 4.2+版,他们引入了一个新功能:,这意味着现在您可以一次性完成您想要的任务:
db.collection.updateOne({ 'item_id' : '12345' },
[
{ '$set': { 'user_scores.GHI': 20 } },
{ '$set': { 'total_score': { '$sum': [ "$user_scores.GHI", "$user_scores.ABC", "$user_scores.GHI"] } } },,
]);
不幸的是,对于较小的Mongo版本,这是不可能的,因此如果是这种情况,您将不得不继续使用您的解决方案,将其拆分为两个操作
编辑:
对于动态更新,我们可以这样使用:
好极了我在4.2.3中使用Mongo Cloud,所以这将非常好。我剩下的问题是关于设置为$sum的语法。1.在第二行$set中,出现一个错误
“total_score.$sum'中以美元($)为前缀的字段'$sum'对于存储无效。”
2。我仍然需要单独查询并知道存在哪些用户分数,才能写出这一行。有没有一种方法可以让我基本上说“对于所有用户的分数值”?是的,我们可以,我在我的答案中添加了一个编辑。借助聚合表达式的强大功能,它非常简单!
db.collection.updateOne(
{'item_id': '12345'},
[
{'$set': {'user_scores.GHI': 20}},
{
'$set':
{
'total_score': {
'$sum': {
'$map': {
'input': {'$objectToArray': '$user_scores'},
'as': 'score',
'in': '$$score.v'
}
}
}
}
}
]);