我正在创建一个同步系统,其中有以下类型的数据:
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)
关于如何构建查询有什么想法吗?
如果您想在一个查询中执行此操作,一种选择是使用带有
$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 示例中的工作原理