我尝试用聚合管道编写更新,但我不知道如何做(除了这个简单的更新之外,我还需要有条件的更新——我只是一个例子)。我尝试了许多变体,但仍然不起作用。我想更新元素是与“judge_id”匹配的嵌套数组的一个元素
如何使用管道语法进行此更新(我需要它进行条件更新)
position.update_one(
filter={'position': 1,
'scores.judge_id': 1},
update={'$set': {'scores.$.evaluation': 10}},
)
我从经典更新中得到这样的结果:
# original data -> it is O.K. always :)
{'position': 1, 'scores': [{'judge_id': 1, 'evaluation': 1}, {'judge_id': 2, 'evaluation': 2}]}
# it is classic update with use $ operator -> it is O.K. judge_id==1 updated!
{'position': 1, 'scores': [{'judge_id': 1, 'evaluation': 10}, {'judge_id': 2, 'evaluation': 2}]}
但使用聚合管道更新-我无法实现同样的效果
# now I trying pipeline update #1 -> INVALID both evaluation is updated not judge_id==1
# how to select only one element!
{'position': 1, 'scores': [{'judge_id': 1, 'evaluation': 10}, {'judge_id': 2, 'evaluation': 10}]}
# now I trying pipeline update #2 -> INVALID strange results both updated again with full tables.
# if/then/else not works like I think
{'_id': ObjectId('5ef4f432f342e09a163bc921'), 'position': 1, 'scores': [{'judge_id': 1, 'evaluation': [10, 10]}, {'judge_id': 2, 'evaluation': [10, 10]}], 'score': {'0': {'evaluation': 10}}}
下面是完整的代码和数据
import pymongo
client = pymongo.MongoClient()
client.drop_database('delete_it')
db = client.delete_it
db.position.create_index('position', unique=True)
position: pymongo.collection.Collection = db.position
position.insert_one(
document={'position': 1,
'scores': [{'judge_id': 1, 'evaluation': 1},
{'judge_id': 2, 'evaluation': 2}]}
)
r = position.find_one(
filter={'position': 1},
projection={'_id': False}
)
print(r)
position.update_one(
filter={'position': 1,
'scores.judge_id': 1},
update={'$set': {'scores.$.evaluation': 10}},
)
r = position.find_one(
filter={'position': 1},
projection={'_id': False}
)
print(r)
position.update_one(
filter={'position': 1,
'scores.judge_id': 1},
update=[{'$set': {'scores.evaluation': 10}}],
)
r = position.find_one(
filter={'position': 1},
projection={'_id': False}
)
print(r)
# conditional update
position.update_one(
filter={'position': 1,
'scores.judge_id': 1},
update=[
{'$set': {'scores.evaluation': {'$cond': {
'if': {'$eq': ['$scores.judge_id', 1]},
'then': 10,
'else': '$scores.evaluation'
}}}}
],
)
r = position.find_one(
filter={'position': 1},
projection={'_id': False}
)
print(r)
我首先找到了这个解决方案。它比concatArrays
慢一点
position.update_one(
filter={'position': 1,
'scores.judge_id': 1},
update=[
{
'$set': {
'scores': {
'$map': {
'input': '$scores',
'in': {
'$mergeObjects': [
'$$this', {
'evaluation': {
'$cond': {
'if': {'$eq': ['$$this.judge_id', 1]},
'then': NEW_EVALUATION,
'else': '$$this.evaluation'
}
}
}
]
}
}
}
}
}
],
)
我找到了第二个更快的解决方案
position.update_one(
filter={'position': 1,
'scores.judge_id': 1},
update=[
{
'$set': {
'scores': {
'$concatArrays': [
{
# change evaluation of one element
'$map': {
# array with one element only matching
'input': {
'$filter': {
'input': '$scores',
'cond': {'$eq': ['$$this.judge_id', 1]}
}
},
'in': {
'$mergeObjects' : [
'$$this', {'evaluation': 10}
]
}
}
},
# array of rest elements not matching
{
'$filter': {
'input': '$scores',
'cond': {'$ne': ['$$this.judge_id', 1]}
}
}
]
}
}
}
],
)
创建聚合不是为了更改数据,而是为了接收数据。您可以使用所需形式的聚合创建数据的快照(例如,满足或不满足条件的文档标识符数组),然后使用forEach运算符或您的语言可用的其他迭代器处理它们。我不熟悉python,但这里有一个javascript示例
那就是你想要的。但这不是正确的方法,它使开发变得过于复杂。如果分数数组中的对象包含标识符,这将大大简化过程。否则,有必要直接在管道中处理字段名的替换。底线是,必须首先将数组展开为对象,然后对其进行更改并将其返回到原始状态
聚合管道不支持位置更新语法
我被告知另一种做类似事情的方法是使用
$concatArrays
,但我没有调查这种方法的确切细节相关问题 更多 >
编程相关推荐