После замеров скорости работы скриптов на питоновском профайлере cProfile оказалось, что самая затратная операция - count(). Вызвать .count у запроса - в 4 раза дороже, чем .next().
Оказалось, что вызовы count занимают в моём скрипте больше половины времени, 160 секунд из 290.
Переделал скрипт. Было:
for node in db.nodes.find(...):
bad_neighbors = db.nodes.find({'_id': {'$in': node['neighbors']}})
while bad_neighbors.count() > 0:
for neighbor in bad_neighbors:
...
bad_neighbors.rewind()
Заменил на счётчик в виде переменной, который запускается в цикле.
for node in db.nodes.find(...):
bad_neighbors = db.nodes.find({'_id': {'$in': node['neighbors']}})
bad_neighbors_count = 0
while True:
for neighbor in bad_neighbors:
bad_neighbors_count += 1
...
if bad_neighbors_count == 0:
break
bad_neighbors.rewind()
Суммарное время сократилось до 134 секунд, из них 107 - на cursor.next, в которых один и тот же объект достаётся минимум дважды. Есть куда оптимизировать.