简体   繁体   English

有没有更简单的方法来使用 python 更新 MongoDB 中的整个记录​​?

[英]Is there a simpler way to update an entire record in MongoDB using python?

My use case is this: I am passing a json payload into a python program which conditionally upserts the record into my MongoDB collection.我的用例是这样的:我将一个 json 有效负载传递给一个 python 程序,该程序有条件地将记录插入到我的 MongoDB 集合中。 It checks for a unique combination in the collections records and updates the record if the 'timestamp' is greater, upserts if the combination is not found and retains the record if 'timestamp' is lower.它检查集合记录中的唯一组合,如果“时间戳”更大,则更新记录,如果未找到组合,则更新记录,如果“时间戳”更低,则保留记录。

payload = {'stCode': 'ABC1', 'skCode': 'CDE2', 'batchCode': 'FGH3', ts: '2022/07/04 09:48:36'}
inp_ts = payload['ts']
inp_ts = datetime.strptime(inp_ts, '%Y/%m/%d %H:%M:%S')
inp_ts_iso = inp_ts.isoformat()

stCode = payload['stCode']
skCode = payload['skCode']
batchCode = payload['batchCode']
    
result = collection.find_one({'stCode': stCode,'skCode': skCode, 'batchCode': batchCode }) 

if result is None:
    collection.insert_one(payload) #Condition1: Insert record if not present
        
else:
    result_ts = result['ts']
    result_ts = datetime.strptime(result_ts, '%Y/%m/%d %H:%M:%S')
    result_ts_iso = result_ts.isoformat() #since the date is stored as string in the database
        
    if inp_ts_iso > result_ts_iso:
        temps={"storeCode": storeCode,"skuCode": skuCode, "batchCode": batchCode }, {"$set": payload}
        collection.update_one(*temps) #Condition2: Update record if timestamp is older
        print('Records successfully written to MongoDB')
            
    else:
        print("no records written") #Condition3: Do nothing if timestamp is newer

I know that it isn't the prettiest of the code but I would like it to be more simple and short.我知道它不是最漂亮的代码,但我希望它更简单、更简短。 Is there a way of achieving the same by without having Lambda to do the logic test but rather do it on Mongo's side itself?有没有办法通过让 Lambda 进行逻辑测试而不是在 Mongo 本身进行测试来实现相同的目标? I have seen complex update_one queries but I couldn't work it for my benefit.我见过复杂的 update_one 查询,但我无法为我的利益工作。 Thanks in advance!提前致谢!

Yes you can, you can do it in a few different ways, but they all involve using pipelined updates which allows you to use aggregation operators in your updates.是的,您可以,您可以通过几种不同的方式进行操作,但它们都涉及使用流水线更新,这允许您在更新中使用聚合运算符。

Here is what I consider to be the most elegant way of achieving this: (summary of the approach at the bottom)这是我认为实现这一目标的最优雅的方法:(底部方法的摘要)

const payload = {'stCode': 'ABC1', 'skCode': 'CDE2', 'batchCode': 'FGH3', ts: '2022/07/04 09:48:36'}
const stCode = payload['stCode']
const skCode = payload['skCode']
const batchCode = payload['batchCode']

db.collection.updateOne(
    {'stCode': stCode,'skCode': skCode, 'batchCode': batchCode },
    [
      {
        "$replaceRoot": {
          "newRoot": {
            "$mergeObjects": [
              "$$ROOT",
              {
                $cond: [
                  {
                    $gt: [
                      "$ts",
                      payload.ts
                    ]
                  },
                  {},
                  payload
                ]
              }
            ]
          }
        }
      }
    ],
    {
      "upsert": true
    }
)

Or with pymongo :或使用pymongo

payload = {'stCode': 'ABC1', 'skCode': 'CDE2', 'batchCode': 'FGH3', ts: '2022/07/04 09:48:36'}
stCode = payload['stCode']
skCode = payload['skCode']
batchCode = payload['batchCode']

collection.update_one(
    {'stCode': stCode, 'skCode': skCode, 'batchCode': batchCode},
    [
        {
            "$replaceRoot": {
                "newRoot": {
                    "$mergeObjects": [
                        "$$ROOT",
                        {
                            "$cond": [
                                {
                                    "$gt": [
                                        "$ts",
                                        payload['ts']
                                    ]
                                },
                                {},
                                payload
                            ]
                        }
                    ]
                }
            }
        }
    ],
    upsert=True
)

Mongo Playground蒙戈游乐场

So we use $replaceRoot in the update, the new root is a merge of two objects.所以我们在更新中使用$replaceRoot ,新的根是两个对象的合并。 The first object is the $$ROOT parameter, so if the document does not exist it's basically an empty object, if it does exist then it's the original object, the idea behind this being the first object is to preserve the _id field (and any other required pre existing fields) that could exist on the object, this is why we don't just use the new object in the $replaceRoot as it will generate a new _id .第一个对象是$$ROOT参数,所以如果文档不存在它基本上是一个空对象,如果它确实存在那么它是原始对象,这背后的想法是第一个对象是保留_id字段(以及任何其他必需的预先存在的字段)可能存在于对象上,这就是为什么我们不只在$replaceRoot中使用新对象,因为它会生成一个新的_id

The second object is based on a condition using $cond , if the existing ts is bigger then we use an empty object, which does not change the original $$ROOT , else we just use the new payload which will overwrite any of the existing fields.第二个对象基于使用$cond的条件,如果现有的ts更大,那么我们使用一个空对象,它不会更改原始$$ROOT ,否则我们只使用新的有效负载,它将覆盖任何现有字段. As mentioned if the new payload has missing fields they will not be overwritten if this could be the case some minor changes need to be done, but based on your code sample this potential edge case was not handled so I assumed it does not exist.如前所述,如果新的有效负载缺少字段,如果可能需要进行一些小的更改,它们将不会被覆盖,但是根据您的代码示例,未处理此潜在的边缘情况,因此我认为它不存在。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM