首页 > 解决方案 > 更新大批量推文

问题描述

我有超过 75 万条推文。这些推文存储在本地 MongoDB 实例中。我想使用以下代码创建每条推文的时间戳。这些时间戳稍后在其他脚本中使用。

import pymongo
import datetime
import time
import gc
gc.collect()

client = pymongo.MongoClient("localhost:27017")
db = client.copySB #Database
col = db.original_tweets #Collectiion

start_time_total = time.time()

if col.find({"timestamp":{"$exists":False}}):
    for tweet in col.find({"timestamp":{"$exists":False}}):
        try:
            # loop_start_time = time.time()
            tweetId = tweet['id']
            date = datetime.datetime.strptime(tweet['created_at'],'%a %b %d %H:%M:%S +0000 %Y')
            epoch = date.utcfromtimestamp(0)
            timestamp = (date-epoch).total_seconds()
            col.update_one(          {'id': tweetId},
                                     #set - add item to the existing set, if not exist, will create a new field
                                     {'$set': {'timestamp': timestamp }},
                                     upsert=False
                                     )
            # loop_end_time = (time.time()-loop_start_time)/60
            # print("----------------------------------------------------")
            # print(f'Mongo data update loop took {loop_end_time} seconds')
            # print ("Total tweets altered in this loop")
            print(len(tweet))
        except pymongo.errors.DuplicateKeyError:
            pass
else:
    pass
end_time_total = (time.time()-start_time_total)/60
print("-----------------------------------")
print(f'Mongo data update took {end_time_total} minutes')
print ("total tweets in the dataset ")
print (col.estimated_document_count())
print ("total valid tweets with timestamp ")
print (col.count_documents({'timestamp' : {'$exists': True}}))
print("-----------------------------------")

这段代码可以很好地满足我的需要,但迭代速度很慢。我看到我的脚本在每个查询中只收集 30 条左右的推文。我整个下午都在运行它,它只更新了 15 万条推文。

为什么我的循环只收集这么少量的推文?我没有指定任何限制,但脚本每个循环只收集少量。

我希望看到我的循环遍历批量大小,但我不确定如何最好地实现它。建议?

编辑:现在我已经接近 30 万条更改的推文,更新过程已经大大减慢。据我了解,脚本是否在更新下一个值之前检查集合中的每条推文?

EDIT-2:使用@BellyBuster 的解决方案后,我能够快速更新我的推文。现在我很好奇在时间戳方面什么是最好的。我的原始代码创建了这个时间戳,1549250444。而新代码创建了这个时间戳,1549248971000。这些值的差异让我很好奇为什么新方法会创建一个更短的时间戳。

最终编辑

我的最终命令:

col.update_many({"timestamp": {"$exists": False}},
                [{'$addFields':
                        {'timestamp':
                            {'$divide':
                               [
                                {'$toLong':
                                    {'$toDate': '$created_at'}
                                },
                                1000
                               ]
                            }
                        }
                    }
                ]
               )

我最初的问题没有得到真正的回答,但这个解决方案更健壮并且运行得很好。

标签: pythonmongodbtwittermongodb-querypymongo

解决方案


假设您有 mongo 4.2 或更高版本,您可以使用$addFields$toDate$ toLong运算符在一个聚合命令中执行此操作;对于 75 万条记录,这在我的笔记本电脑上只需要不到一分钟的时间。

我建议将日期/时间存储为 ISODates 而不是时间戳,但这是另一回事。

ISODate 版本:

col.update_many({"timestamp": {"$exists": False}},
                          [{'$addFields': {'timestamp': {'$toDate': '$created_at'}}}])

时间戳版本:

col.update_many({"timestamp": {"$exists": False}},
                [{'$addFields': {'timestamp': {'$toLong': {'$toDate': '$created_at'}}}}])

完整的数据设置示例:

import pymongo
import datetime
import time
from bson.json_util import dumps

db = pymongo.MongoClient()['mydatabase']
col = db.original_tweets  # Collectiion

operations = []

# Data Setup Only
for i in range(750000):
    operations.append(pymongo.InsertOne(
        {'created_at': datetime.datetime.strftime(datetime.datetime.utcnow(), '%a %b %d %H:%M:%S +0000 %Y')}))
col.bulk_write(operations)

# Update each record without a timestamp from the created_at field
start_time_total = time.time()

col.update_many({"timestamp": {"$exists": False}},
                [{'$addFields': {'timestamp': {'$toLong': {'$toDate': '$created_at'}}}}])

# Output

end_time_total = (time.time() - start_time_total) / 60
print("-----------------------------------")
print(f'Mongo data update took {end_time_total} minutes')
print("total tweets in the dataset ")
print(col.estimated_document_count())
print("total valid tweets with timestamp ")
print(col.count_documents({'timestamp': {'$exists': True}}))
print("-----------------------------------")

print(dumps(col.find_one({}, {'_id': 0}), indent=4))

给出:

-----------------------------------
Mongo data update took 0.4217496275901794 minutes
total tweets in the dataset 
750000
total valid tweets with timestamp 
750000
-----------------------------------
{
    "created_at": "Sun Dec 22 12:19:48 +0000 2019",
    "timestamp": 1577017188000
}

推荐阅读