MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Como encontrar no mongodb o último item de uma matriz?


Sei que esta pergunta é antiga, mas a encontrei no google depois de responder a uma nova pergunta semelhante . Então eu pensei que isso merecia o mesmo tratamento.

Você pode evitar o impacto no desempenho de $where usando agregado em vez de:
db.example.aggregate([

    // Use an index, which $where cannot to narrow down
    {$match: { "comments.by": "Abe" }},

    // De-normalize the Array
    {$unwind: "$comments"},

    // The order of the array is maintained, so just look for the $last by _id
    {$group: { _id: "$_id", comments: {$last: "$comment"} }},

    // Match only where that $last comment by `by.Abe`
    {$match: { "comments.by": "Abe" }},

    // Retain the original _id order
    {$sort: { _id: 1 }}

])

E isso deve rodar em torno de $where já que conseguimos restringir os documentos que tinham um comentário de "Abe" em primeiro lugar. Conforme avisado, $where vai testar todos os documentos da coleção e nunca usar um índice, mesmo que exista um para ser usado.

Claro, você também pode manter o documento original usando a técnica descrita aqui também, então tudo funcionaria como um find() .

Apenas alimento para o pensamento para quem encontrar isso.

Atualização para versões modernas do MongoDB


As versões modernas adicionaram o $redact expressão de pipeline, bem como $arrayElemAt (o último a partir de 3.2, então essa seria a versão mínima aqui) que, em combinação, permitiria que uma expressão lógica inspecionasse o último elemento de um array sem processar um $unwind palco:
db.example.aggregate([
  { "$match": { "comments.by": "Abe" }},
  { "$redact": {
    "$cond": {
      "if": {
        "$eq": [
          { "$arrayElemAt": [ "$comments.by", -1 ] },
          "Abe"
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

A lógica aqui é feita em comparação onde $arrayElemAt está obtendo o último índice do array -1 , que é transformado em apenas uma matriz dos valores no "by" propriedade via $map . Isso permite a comparação do valor único com o parâmetro necessário, "Abe" .

Ou até um pouco mais moderno usando $expr para MongoDB 3.6 e superior:
db.example.find({
  "comments.by": "Abe",
  "$expr": {
    "$eq": [
      { "$arrayElemAt": [ "$comments.by", -1 ] },
      "Abe"
    ]
  }
})

Esta seria de longe a solução de melhor desempenho para combinar o último elemento dentro de uma matriz e, na verdade, deve substituir o uso de $where na maioria dos casos e especialmente aqui.