更新:如果您阅读此书以提高插入/更新速度,请首先通过从python控制台运行
pymongo.has_c()
来检查系统上是否启用了pymongo C扩展。如果解析为False
,则需要使用C扩展名编译pymongo或执行pip install --upgrade pymongo
它将我的工作流程从10K行上的17秒提高到了大约0.57秒。
我有成千上万个txt文件,其中包含数百万行要尝试导入mongodb集合的数据。
我当前正在使用以下 def :
import re, pymongo
coll = pymongo.MongoClient().database.collection
rx = re.compile(r'[:; ]')
rx_email = re.compile(r'\S+@\S+\.\S+$')
def parser(path):
with open(path, "rb") as f:
for line in f:
try:
fields = rx.split(line.decode('utf-8'))
email = ''
username = ''
for field in fields:
if rx_email.match(field):
email = field
elif field != fields[-1]:
username = field
password = fields[-1]
if email:
coll.find_one_and_update({'email': email}, {'$addToSet': {'passwords': password}}, upsert=True)
elif username:
coll.find_one_and_update({'username': username}, {'$addToSet': {'passwords': password}}, upsert=True)
else:
pass
except UnicodeDecodeError:
pass
if __name__ == "__main__":
parser('path/to/file.txt')
当我尝试在具有10K行的文件上运行脚本时,花了74.58974479999999秒。我认为这是由于插入时MongoDB必须匹配的项目数量导致的? 在没有数据库交互的情况下运行相同的循环花费了0.022998秒。
EDIT :如Fast or Bulk Upsert in pymongo中所建议,我还尝试将UpdateOne
与bulk_write
一起使用,如下所示:
def parser(path):
ops = []
with open(path, "rb") as f:
for line in f:
if (len(ops) == 1000):
LOCAL_DB.bulk_write(ops, ordered=False)
ops = []
try:
fields = rx.split(line.decode('utf-8'))
email = ''
username = ''
for field in fields:
if rx_email.match(field):
email = field
elif field != fields[-1]:
username = field
password = fields[-1]
if email:
pass
ops.append((UpdateOne({'identifier': email}, {'$addToSet': {'passwords': password}}, upsert=True)))
elif username:
pass
ops.append((UpdateOne({'identifier': username}, {'$addToSet': {'passwords': password}}, upsert=True)))
else:
pass
except UnicodeDecodeError:
pass
完成1万行的时间为17秒,但是这会减慢我尝试更新的文件和行的数量。
有没有更好(并且希望更快)的方法?
一些要求:
答案 0 :(得分:1)
在@JohnnyHK的评论指导下,我似乎可以通过对初始代码执行以下操作来使我的upsert
初始时间从10秒的约74秒缩短至〜0.5秒:
import re, pymongo
rx = re.compile(r'[:; ]')
rx_email = re.compile(r'\S+@\S+\.\S+$')
def parse(path):
ops = []
with open(path, "rb") as f:
for line in f:
if (len(ops) == 1000):
pymongo.MongoClient().database.collection.bulk_write(ops, ordered=False)
ops = []
try:
fields = rx.split(line.decode('utf-8'))
email = ''
username = ''
for field in fields:
if rx_email.match(field):
email = field
elif field != fields[-1]:
username = field
password = fields[-1]
if email:
pass
ops.append((pymongo.UpdateOne({'_id': email}, {'$addToSet': {'passwords': password}}, upsert=True)))
elif username:
pass
ops.append((pymongo.UpdateOne({'_id': username}, {'$addToSet': {'passwords': password}}, upsert=True)))
else:
pass # logic removed
except UnicodeDecodeError:
pass # logic removed
if __name__ == "__main__":
parse(path/to/file.txt)
我发现系统上缺少pymongo C extensions:
>>> import pymongo
>>> pymongo.has_c()
>>> False
从那里我做了pip install --upgrade pymongo
(对我来说很幸运)
然后解析为True
对于唯一字段,我还使用_id
代替了identifier
,从而进一步提高了速度。
希望这可以帮助人们前进。我将根据自己的经验更新更多的发现。