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

Filtrar resultados pelo valor do campo de entrada da última matriz


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.