A milhagem pode variar nisso e pode acontecer que "atualmente" o processo que você está seguindo seja "mais adequado", pelo menos. Mas provavelmente podemos fazer de forma mais eficiente.
O que você pode fazer agora
Desde que seus arrays já estejam "classificados" usando o
$classificar
modificador com $push
, então você provavelmente pode fazer isso:db.somedb.find(
{
"partn.is_partner": true,
"$where": function() {
return this.partn.slice(-1)[0].is_partner == true;
}
},
{ "partn": { "$slice": -1 } }
)
Então, desde que
partn,is_partner
é "indexado", isso ainda é bastante eficiente, pois essa condição de consulta inicial pode ser atendida usando um índice. A parte que não pode é o $where
cláusula aqui que usa avaliação JavaScript. Mas o que essa segunda parte no
$where
está fazendo é simplesmente "cortar" o último elemento do array e testar seu valor do is_partner
propriedade para ver se é verdade. Somente se essa condição também for atendida, o documento será devolvido. Há também o
$slice
operador de projeção. Isso faz a mesma coisa ao retornar o último elemento da matriz. As correspondências falsas já são filtradas, portanto, isso mostra apenas o último elemento onde for verdadeiro. Combinado com o índice, conforme mencionado, isso deve ser bem rápido, pois os documentos já foram selecionados e a condição JavaScript apenas filtra o resto. Observe que, sem outro campo com uma condição de consulta padrão para corresponder, um
$where
cláusula não pode usar um índice. Portanto, sempre tente usar "com moderação" com outras condições de consulta em vigor. O que você pode fazer no futuro
Next Up, embora não disponível no momento da escrita, mas certamente em um futuro próximo será o
$slice
operador para a estrutura de agregação. Isso está atualmente no ramo de desenvolvimento, mas aqui está uma olhada em como ele funciona:db.somedb.aggregate([
{ "$match": { "partn.is_partner": true } },
{ "$redact": {
"$cond": {
"if": {
"$anyElementTrue": {
"$map": {
"input": { "$slice": ["$partn",-1] },
"as": "el",
"in": "$$el.is_partner"
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}},
{ "$project": {
"partn": { "$slice": [ "$partn",-1 ] }
}}
])
Combinando esse
$slice
dentro de um $redact
etapa aqui permite que os documentos sejam filtrados com uma condição lógica, testando o documento. Neste caso, o $slice
produz um array de elemento único que é enviado para $ mapa
para extrair apenas o único is_partner
value (ainda como um array). Como este ainda é um array de elemento único na melhor das hipóteses, o outro teste é $anyElementTrue
o que torna este um resultado booleano singular, adequado para $cond
. O
$redact
aqui decide sobre esse resultado se $$KEEP
ou $$PRUNE
o documento a partir dos resultados. Mais tarde, usamos $slice
novamente no projeto para retornar apenas o último elemento do array após a filtragem. Isso funciona para ser exatamente o que a versão JavaScript faz, com a exceção de que isso está usando todos os operadores codificados nativos e, portanto, deve ser um pouco mais rápido que a alternativa JavaScript.
Ambos os formulários retornam seu primeiro documento conforme o esperado:
{
"_id" : 0,
"partn" : [
{
"date" : ISODate("2015-07-28T00:59:14.963Z"),
"is_partner" : true
},
{
"date" : ISODate("2015-07-28T01:00:32.771Z"),
"is_partner" : false
},
{
"date" : ISODate("2015-07-28T01:15:29.916Z"),
"is_partner" : true
},
{
"date" : ISODate("2015-08-05T13:48:07.035Z"),
"is_partner" : false
},
{
"date" : ISODate("2015-08-05T13:50:56.482Z"),
"is_partner" : true
}
]
}
O grande problema aqui com ambos é que sua matriz já deve estar classificada para que a data mais recente seja a primeira. Sem isso, você precisa da estrutura de agregação para
$sort
a matriz, assim como você está fazendo agora. Não é realmente eficiente, então é por isso que você deve "pré-classificar" sua matriz e manter a ordem em cada atualização.
Como um truque útil, isso realmente reordenará todos os elementos da matriz em todos os documentos de coleção em uma instrução simples:
db.somedb.update(
{},
{ "$push": {
"partn": { "$each": [], "$sort": { "date": 1 } }
}},
{ "multi": true }
)
Portanto, mesmo que você não esteja "enviando" um novo elemento para um array e apenas atualizando uma propriedade, você sempre pode aplicar essa construção básica para manter o array ordenado como você deseja.
Vale a pena considerar, pois deve tornar as coisas muito mais rápidas.