如果不存在则插入,根据附加条件更新[mongoDB]

问题描述 投票:0回答:1

我正在创建一个同步系统,其中有以下类型的数据:

records = [
    {'_id': 0, 'name': 'John', 'sync_date': '2022-01-01 00:00:00'}, 
    {'_id': 1, 'name': 'Paul', 'sync_date': '2021-11-11 11:11:11'}, 
    {'_id': 2, 'name': 'Anna', 'sync_date': '2012-12-12 12:12:12'}
]

想法是 (1) 如果没有

_id
0 或 1(要插入/更新的记录的 ID)的记录,则插入,或者仅当
sync_date
records
大于时才更新它们收藏中的那一张。例如,如果我们的收藏中有这个:

[
    {'_id': 0, 'name': 'John', 'sync_date': '2000-01-01 00:00:00'}, 
    {'_id': 1, 'name': 'Paul', 'sync_date': '2023-01-01 23:23:23'}
]

然后它应该 (1) 更新 John 的实例,因为集合的

sync_date
比我们记录中的更低(时间更早),并且 忽略 Paul 的记录,因为集合实例中可用的
sync_date
更大 (时间早)比我们记录中的时间早。此外,它应该插入 Anna 的实例,因为它根本不在集合中。

我看到的问题是,使用

replace_one
update_one
方法,如果添加
upsert=True
那么它不仅会在它不在集合中时插入它,而且当
sync_date
大于记录时也会插入它。参见:

for record in records:
    collection.replace_one({
            '_id': record['_id'], 
            'sync_date': {'$lt': record['sync_date']}
            },
        {'upsert': True})

我知道一定有一种更有效的方法使用聚合查询,但目前这是我的临时解决方案:

for record in records:
    if not collection.find_one({'_id': record['_id']}):
        collection.insert_one(record)
    else:
        collection.update_one(
            {'_id': record['_id'], 'sync_date': {'$lt': record['sync_date']}},
            record)

关于如何构建查询有什么想法吗?

python python-3.x mongodb mongodb-query pymongo
1个回答
0
投票

如果您想在一个查询中执行此操作,一种选择是使用带有

$merge
:

的聚合
db.collection.aggregate([
  {
    $match: {
      _id: {
        $in: [
          0,
          1,
          2
        ]
      }
    }
  },
  {
    $group: {
      _id: null,
      docs: {
        $push: "$$ROOT"
      }
    }
  },
  {
    "$addFields": {
      records: [
        {
          "_id": 0,
          "name": "John",
          "sync_date": "2022-01-01 00:00:00"
        },
        {
          "_id": 1,
          "name": "Paul",
          "sync_date": "2021-11-11 11:11:11"
        },
        {
          "_id": 2,
          "name": "Anna",
          "sync_date": "2012-12-12 12:12:12"
        }
      ]
    }
  },
  {
    $unwind: "$records"
  },
  {
    $project: {
      _id: 0,
      record: "$records",
      docs: {
        $filter: {
          input: "$docs",
          cond: {
            $and: [
              {
                $eq: [
                  "$$this._id",
                  "$records._id"
                ]
              },
              {
                $gt: [
                  "$$this.sync_date",
                  "$records.sync_date"
                ]
              }
            ]
          }
        }
      }
    }
  },
  {
    $replaceRoot: {
      newRoot: {
        $mergeObjects: [
          "$record",
          {
            $first: "$docs"
          }
        ]
      }
    }
  },
  {
    $merge: {
      into: "collection"
    }
  }
])

查看它在 playground 示例中的工作原理

© www.soinside.com 2019 - 2024. All rights reserved.