Para responder a Q sobre $in....
Fiz alguns testes de performance com o seguinte cenário:
~24 milhões de documentos em uma coleção
Procure 1 milhão desses documentos com base em uma chave (indexada)
Usando o driver CSharp do .NET
Resultados:
Consultando 1 de cada vez, encadeamento único:109s
Consultando 1 de cada vez, multiencadeamento:48s
Consultando 100K de cada vez usando $in, encadeamento único=20s
Consultando 100K de cada vez usando $in, multi threaded=9s
Portanto, desempenho visivelmente melhor usando um grande $in (restrito ao tamanho máximo da consulta).
Atualização: Seguindo os comentários abaixo sobre como $in funciona com diferentes tamanhos de blocos (consultas multi-thread):
Consultando 10 por vez (100.000 lotes) =8,8s
Consultando 100 por vez (10.000 lotes) =4,32s
Consultando 1.000 por vez (1.000 lotes) =4,31s
Consultando 10.000 por vez (100 lotes) =8,4s
Consultando 100.000 por vez (10 lotes) =9s (por resultados originais acima)
Portanto, parece haver um ponto ideal para quantos valores agrupar em uma cláusula $in versus o número de viagens de ida e volta