Digamos que eu tenha um documento com esta estrutura:
_id: ObjectId,
user_id: int,
deleted: bool,
'additional.id': string, // optional field
synced_at: Date //optional field
documento de exemplo será:
{
"_id" : ObjectId("5dce551d6ad5bb1fd829bd77"),
"user_id" : NumberInt(1),
"additional" : {
"id" : "hahahah"
},
"deleted" : false,
"synced_at" : ISODate("2023-12-19T19:21:26.678+0000")
}
e preciso contar todos os documentos que correspondem a esta consulta:
aggregate(
[
{
$match: {
user_id: 1,
deleted: false,
"additional.id" : {$exists : true},
synced_at : {
$gte: new Date(new Date() - 7 * 60 * 60 * 24 * 1000)
},
}
}
,
{
$count : "productsCount"
}
]
)
Então criei um índice como este:
createIndex(
{
"user_id": 1,
"deleted": 1,
"additional.id": 1,
"synced_at": -1,
},
{
partialFilterExpression: {
"deleted" : false,
"additional.id" : {
"$exists" : true
},
"synced_at" : {
"$exists" : true
}
}
)
e digamos que o resultado da consulta seja 20.000. Quando executo uma consulta, .explain("executionStats")
posso ver isso totalKeysExamined
e totalDocsExamined
ambos são iguais a 20.000, e se não me engano, significa que 20 mil produtos foram retirados do índice e, além disso, o mongo fez uma pesquisa sobre todos esses 20 mil produtos. Verificando mais profundamente as estatísticas de execução, posso ver Fetch
o estágio no topo do índice:
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"additiona.id" : {
"$exists" : true
}
,
]
},
Então, novamente, se não me engano, isso significa que o Mongo pega todos os documentos necessários (20k) do índice, mas por algum motivo verifica adicionalmente cada um dos 20k produtos, mesmo "additiona.id" : {"$exists" : true}
que já esteja empartialFilterExpression
Posso evitar esse estágio de busca de alguma forma? Estou no Mongo 5.
Os índices MongoDB são estruturas semelhantes a árvores b. O valor na árvore de um documento é a lista do valor de cada campo indexado.
Não há nenhum "valor" que possa ser inserido quando um documento não contém um dos campos do índice, por isso
null
é utilizado.Isso significa que a entrada do índice
{user_id:1, deleted: false, additional:{id:null}}
será idêntica ao índice de
{user_id:1, deleted: false}
Ao usar o operador $exists, o primeiro desses documentos corresponderá, enquanto o segundo não.
Isso significa que o índice não pode determinar quais documentos correspondem sem ler alguns deles.
Na linguagem de consulta MongoDB, a verificação como
null
valor corresponderá a ambos explicitamente definidos como nulos e inexistentes.Isso significa que se você mudar
para
O executor da consulta pode identificar as correspondências sem precisar examinar os documentos.
Consulte https://www.mongodb.com/docs/manual/tutorial/query-for-null-fields/#query-for-null-or-missing-fields
Dependendo da versão, o teste
null
também pode fazer com que a consulta não seja coberta.Se você tiver certeza de que qualquer valor existente de "additional.id" será uma string, poderá explorar a sensibilidade de tipo dos operadores de consulta do MongoDB e fazer esse teste:
Isso corresponderá a qualquer string, evitando o bug nulo e a verificação de existência.