【问题标题】:Is it possible to call functions in pymongo update operators?是否可以在 pymongo 更新运算符中调用函数?
【发布时间】:2020-05-01 10:30:24
【问题描述】:

我尝试使用 pyMongo 向 MongoDB 中的文档添加另一个字段。作为示例,我从here 获得了随机用户,我将在“注册”部分添加另一个字段:

from datetime import datetime
from pymongo import MongoClient 

client = MongoClient('localhost', 27017)
db = client['metadata']
db.users.update_many({},
[{
    "$set":{
        "registered.newField" : "$registered.date"        
    }
}])

这是完美的,但每当我要使用另一个函数并使用 "$registered.date" 作为输入参数时,似乎 "registered.date" 不再被解析。

db.users.update_many({},
[{
    "$set":{    
        "registered.newField" : 
            datetime.strptime(
              "$registered.date" ,
              "%Y-%m-%dT%H:%M:%S.%fZ").year 
    }
}])

这个抛出异常:

时间数据“$registered.date”与格式不匹配 '%Y-%m-%dT%H:%M:%S.%fZ'

这是文档的样子:

 {
      "gender": "male",
      "name": {
        "title": "mr",
        "first": "brad",
        "last": "gibson"
      },    
      "registered": {
        "date": "2002-05-21T10:59:49.966Z",
        "age": 17
      },
      .....
}

【问题讨论】:

    标签: python mongodb mongodb-query aggregation-framework pymongo


    【解决方案1】:

    您实际上不能在聚合管道中的现有字段值上使用编程语言的函数。那是什么意思呢? - 当你写这样的东西时:

    db.users.update_many({},
    [{
        "$set":{    
            "registered.newField" : 
                datetime.strptime(
                  "$registered.date" ,
                  "%Y-%m-%dT%H:%M:%S.%fZ").year 
        }
    }])
    

    然后是下面一行的函数:

    datetime.strptime( "$registered.date" , "%Y-%m-%dT%H:%M:%S.%fZ").year 
    

    将在应用程序服务器中执行并在查询命中数据库之前生成结果,因此该结果将更新为"registered.newField" 字段。由于此操作是在命中 DB 之前发生的,因此您的查询将无法获得 "$registered.date" 值。所以在python中datetime.strptime().year的执行实际上是将"$registered.date"视为一个字符串,而不是获取字段值然后转换它。 由于字符串"$registered.date" 的格式不是“%Y-%m-%dT%H:%M:%S.%fZ”,因此该函数将失败。

    测试 - 我会说你可以尝试传入一个硬编码的值来像 datetime.strptime( "2002-05-21T10:59:49.966Z" , "%Y-%m-%dT%H:%M:%S.%fZ").year 这样运行,然后 .update() 肯定会工作。

    分辨率:

    当您使用 datetime.strptime().year 来获取年份时,您实际上会使用聚合运算符 $year

    db.users.update_many({}, [
      {
        $set: {
          "registered.newField": { $year: "$registered.date" }
        }
      }
    ]);
    

    测试: mongoplayground

    我假设您的字段"$registered.date" 的类型为Date 类似于ISODate('2002-05-21T10:59:49.966Z'),如果无论如何它是字符串类型,则使用$dateFromString 转换为Date 然后获取年份:

    db.users.update_many({}, [
      {
        $set: {
          "registered.newField": {
            $year: {
              $dateFromString: {
                dateString: "$registered.date"
              }
            }
          }
        }
      }
    ]);
    

    测试: mongoplayground

    【讨论】:

      【解决方案2】:

      是的,您可以在 pymongo 更新中绝对调用函数;你不能做的是在你的例子中改变规则并依靠$ 运算符来获取前一个阶段的值以传递给函数。

      此代码将起作用;如果您有一个庞大的数据集,您可以以类似的方式通过批量更新进行更新。

      from pymongo import MongoClient
      import datetime
      import pprint
      
      db = MongoClient("mongodb://localhost:27019/")['mydatabase']
      
      db.users.insert_one( {
            "gender": "male",
            "name": {
              "title": "mr",
              "first": "brad",
              "last": "gibson"
            },
            "registered": {
              "date": "2002-05-21T10:59:49.966Z",
              "age": 17
            }
      })
      
      for record in db.users.find():
          try:
              date_str = record['registered']['date']
          except KeyError:
              continue
          year = datetime.datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%fZ").year
          db.users.update_one({'_id': record['_id']}, {'$set': {'registered.newField': year}})
      
      pprint.pprint(db.users.find_one())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-02-06
        • 1970-01-01
        • 2014-11-04
        • 2017-07-29
        • 2021-08-06
        • 2018-07-03
        • 1970-01-01
        • 2021-09-18
        相关资源
        最近更新 更多