Итак, наконец, я смог провести все тесты, вот вся версия, которую я написал, благодаря ответу Willis и результату:
Исходный совокупный запрос
mongo_query = [
{"$match": group_filter},
{"$sort": {"timestamp": -1}},
{"$group": {"_id": "$internal_id", "doc": {"$first": "$$ROOT"}}},
]
res = mongo.db[self.factory.config.mongo_collection].aggregate(mongo_query)
res = await res.to_list(None)
9.61 секунд
Дайте MongoDB подсказку, чтобы использовать правильный индекс (сначала фильтр internal_id)
from bson.son import SON
cursor = mongo.db[self.factory.config.mongo_collection].aggregate(mongo_query, hint=SON([("internal_id", 1), ("timestamp", -1)]))
res = await cursor.to_list(None)
Не работает, MongoDB отвечает с исключением, говоря, что сортировка потребляет слишком много памяти
Разделение агрегации, чтобы сначала найти последнюю временную метку для каждого internal_id
cursor = mongo.db[self.factory.config.mongo_collection].aggregate([{"$group": {"_id": "$internal_id", "timestamp": {"$max": "$timestamp"}}}])
res = await cursor.to_list(None)
or_query = []
for entry in res:
or_query.append({"internal_id": entry["_id"], "timestamp": entry["timestamp"]})
cursor = mongo.db[self.factory.config.mongo_collection].find({"$or": or_query})
fixed_res = await cursor.to_list(None)
1,88 секунды, намного лучше, но все же не так быстро
Параллельные сопрограммы (и победитель ...)
Между тем, поскольку у меня уже есть список internal_id и я использую асинхронный Python, я выбрал параллельную сопрограмму, получив последнюю запись для одного internal_id сразу:
fixed_res: List[Dict] = []
async def get_one_result(db_filter: Dict) -> None:
""" Coroutine getting one result for each known internal ID """
cursor = mongo.db[self.factory.config.mongo_collection].find(db_filter).sort("timestamp", -1).limit(1)
res = await cursor.to_list(1)
if res:
fixed_res.append(res[0])
coros: List[Awaitable] = []
for internal_id in self.list_of_internal_ids:
coro = get_one_result({"internal_id": internal_id})
coros.append(coro)
await asyncio.gather(*coros)
0,5 с, намного лучше, чем другие
Если у вас нет списка internal_id
Есть альтернатива, которую я не реализовал, но я подтвердил, что вызов выполняется очень быстро: используйте низкий уровень distinct
запятая nd по индексу {internal_id: 1}
для получения списка индивидуальных идентификаторов, затем используйте параллельные вызовы.