Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python MongoDb 4.2使用聚合管道从使用嵌套数组元素选择的常规更新进行更新_Python_Mongodb_Mongodb Query_Python 3.8 - Fatal编程技术网

Python MongoDb 4.2使用聚合管道从使用嵌套数组元素选择的常规更新进行更新

Python MongoDb 4.2使用聚合管道从使用嵌套数组元素选择的常规更新进行更新,python,mongodb,mongodb-query,python-3.8,Python,Mongodb,Mongodb Query,Python 3.8,我尝试用聚合管道编写更新,但我不知道如何做(除了这个简单的更新之外,我还需要有条件的更新——我只是一个例子)。我尝试了许多变体,但仍然不起作用。我想更新元素是与“judge_id”匹配的嵌套数组的一个元素 如何使用管道语法进行此更新(我需要它进行条件更新) 我从经典更新中得到这样的结果: # original data -> it is O.K. always :) {'position': 1, 'scores': [{'judge_id': 1, 'evaluation': 1}, {

我尝试用聚合管道编写更新,但我不知道如何做(除了这个简单的更新之外,我还需要有条件的更新——我只是一个例子)。我尝试了许多变体,但仍然不起作用。我想更新元素是与“judge_id”匹配的嵌套数组的一个元素

如何使用管道语法进行此更新(我需要它进行条件更新)

我从经典更新中得到这样的结果:

# 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)


我首先找到了这个解决方案。它比concatarray慢一点

    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示例

那就是你想要的。但这不是正确的方法,它使开发变得过于复杂。如果分数数组中的对象包含标识符,这将大大简化过程。否则,有必要直接在管道中处理字段名的替换。底线是,必须首先将数组展开为对象,然后对其进行更改并将其返回到原始状态

db.getCollection("test").aggregate(
// Pipeline
[
    // Stage 1
    {
        "$match": {
            "scores.judge_id":1
        }
    },
    // Stage 2 (convert array to object)
    {
        "$project":{
            "_id": "$_id",
            "position": "$position",
            "scores": {
                "$arrayToObject": {
                    "$reduce":{
                        "input": "$scores",
                        "initialValue": [],
                        "in": {
                            "$concatArrays": [
                                "$$value",
                                [{
                                    "k": {
                                        "$toString": "$$this.judge_id"
                                    }, 
                                    "v": "$$this.evaluation"
                                }]
                            ]
                        }
                    }
                }
            }
        }
    },
    // Stage 3 (update evaluation)
    {
        "$set": {
            "scores.1": 10
        }
    },
    // Stage 4 (convert object to array)
    {
        "$project":{
            "_id": "$_id",
            "position": "$position",
            "scores": {
                "$objectToArray": "$scores"
            }
        }
    },
    // Stage 5 (return the name of the fields)
    {
        "$project":{
            "_id": "$_id",
            "position": "$position",
            "scores": {
                "$reduce":{
                    "input": "$scores",
                    "initialValue": [],
                    "in": {
                        "$concatArrays": [
                            "$$value",
                            [{
                                "judge_id": {
                                    "$toDouble": "$$this.k"
                                }, 
                                "evaluation": "$$this.v"
                            }]
                        ]
                    }
                }
            }
        }
    }
]
);

创建聚合不是为了更改数据,而是为了接收数据。您可以使用所需形式的聚合创建数据的快照(例如,满足或不满足条件的文档标识符数组),然后使用forEach运算符或您的语言可用的其他迭代器处理它们。我不熟悉python,但这里有一个javascript示例

那就是你想要的。但这不是正确的方法,它使开发变得过于复杂。如果分数数组中的对象包含标识符,这将大大简化过程。否则,有必要直接在管道中处理字段名的替换。底线是,必须首先将数组展开为对象,然后对其进行更改并将其返回到原始状态

db.getCollection("test").aggregate(
// Pipeline
[
    // Stage 1
    {
        "$match": {
            "scores.judge_id":1
        }
    },
    // Stage 2 (convert array to object)
    {
        "$project":{
            "_id": "$_id",
            "position": "$position",
            "scores": {
                "$arrayToObject": {
                    "$reduce":{
                        "input": "$scores",
                        "initialValue": [],
                        "in": {
                            "$concatArrays": [
                                "$$value",
                                [{
                                    "k": {
                                        "$toString": "$$this.judge_id"
                                    }, 
                                    "v": "$$this.evaluation"
                                }]
                            ]
                        }
                    }
                }
            }
        }
    },
    // Stage 3 (update evaluation)
    {
        "$set": {
            "scores.1": 10
        }
    },
    // Stage 4 (convert object to array)
    {
        "$project":{
            "_id": "$_id",
            "position": "$position",
            "scores": {
                "$objectToArray": "$scores"
            }
        }
    },
    // Stage 5 (return the name of the fields)
    {
        "$project":{
            "_id": "$_id",
            "position": "$position",
            "scores": {
                "$reduce":{
                    "input": "$scores",
                    "initialValue": [],
                    "in": {
                        "$concatArrays": [
                            "$$value",
                            [{
                                "judge_id": {
                                    "$toDouble": "$$this.k"
                                }, 
                                "evaluation": "$$this.v"
                            }]
                        ]
                    }
                }
            }
        }
    }
]
);

聚合管道不支持位置更新语法


我被告知做类似事情的另一种方法是使用
$concatarray
,但我没有研究这种方法的确切细节。

聚合管道不支持位置更新语法


我被告知另一种做类似事情的方法是使用
$concatarray
,但我还没有研究这种方法的确切细节。

Agg管道支持$out和$merge阶段,在Agg管道中更新是什么意思?我的意思是
位置.update\u one(filter={'position':1,'scores.judge\u id':1},update=[{'$set':{'scores.evaluation':10}}],)
作为聚合管道的更新。我指的是。好的,你能用散文表达你的要求吗?什么时候你想更新?在散文中:我想用管道语法更新。我想更新scores数组中的一个对象,该对象的judge_id=1,通过将evaluation设置为10(scores中的其他对象不应更改。在经典语法中,update={}将是
position.update_one(filter={'position':1,'scores.judge_id':1},update={'set':{'scores.$.evaluation':10},)
但是如何在管道语法中做到这一点-我不知道?
位置。更新_one(filter={'position':1,'scores.judge_id':1},update=[{'$set':{???}}],)
我需要管道语法来进行条件更新。@D.SM我找到了一些解决方案,但它非常难看-可能很快:
条件={'$cond':{'if':{$eq':['$$this.judget_id',1]},'then':20,'else':'$$this.evaluation'}}位置。更新位置(过滤器={'position':1,'scores.judget_id':1},更新=[{'$set':{'scores':{'input':'$scores in':{'$mergeObjects':['$$this',{'evaluation':condition}]}],)
注意,我正在使用condition变量简化查询语法。Agg管道支持$out和$merge阶段,在Agg管道中更新意味着什么?我的意思是
position.update_one(filter={'position':1,'scores.judge_id':1},update=[{'set':{'scores scores.evaluation':10}],)
as update with aggregation pipeline。我指的是。好的,你能用散文表达你的要求吗?你什么时候要更新?散文:我想用update with pipeline语法。我想通过将求值设置为10来更新分数数组中的一个对象,该对象的判断id=1(分数中的其他对象不应更改。在经典语法中update={}将是
位置。update_one(filter={'position':1,'scores.judge_id':1},update={'set':{'scores.$.evaluation':10},)
但在管道语法中如何做同样的事情-我不知道?
位置。update_one(filter={'position 1,'scores.judge_id':1},update=[{'$set':{???}}],)
我需要管道语法来进行条件更新。@D.SM我找到了一些解决方案,但它非常难看-可能很快:)请参见:
条件={'$cond':{'if':{'$eq':['$$this.judge_id',1]},'then''20',else':'$$this.evaluation'}位置。更新\u one(过滤器={'position':1,'scores.judge_id':1},update=[{'$set':{'scores':{'$map':{'input':'$scores','in':{'$mergeObjects':['$$this',{'evaluation':condition}],)
注意,我正在使用条件变量简化查询语法。4.2版之后的Mongodb允许管道更新。我不想使用
聚合
但是