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

Como usar $ regex dentro de $ ou como uma expressão de agregação


Tudo dentro de $expr é uma expressão de agregação, e a documentação não pode "dizer que você não pode explicitamente" , mas a falta de qualquer operador nomeado e o problema do JIRA SERVER-11947 certamente dizer isso. Portanto, se você precisar de uma expressão regular, não terá outra opção senão usar $onde em vez de:
db.getCollection('permits').find({
  "$where": function() {
    var description = this.inspections
       .sort((a,b) => b.inspectionDate.valueOf() - a.inspectionDate.valueOf())
       .shift().description;

     return /^Found a .* at the property$/.test(description) ||
           description === "Health Inspection";

  }
})

Você ainda pode usar $expr e expressões de agregação para uma correspondência exata ou apenas mantenha a comparação dentro do $onde de qualquer forma. Mas, neste momento, a única expressão regular que o MongoDB entende é $regex dentro de uma expressão "query" .

Se você realmente "exigir" uma expressão de pipeline de agregação que impede você de usar $where , a única abordagem válida atual é primeiro "projetar" o campo separadamente da matriz e, em seguida, $match com a expressão de consulta regular:
db.getCollection('permits').aggregate([
  { "$addFields": {
     "lastDescription": {
       "$arrayElemAt": [
         "$inspections.description",
         { "$indexOfArray": [
           "$inspections.inspectionDate",
           { "$max": "$inspections.inspectionDate" }
         ]}
       ]
     }
  }},
  { "$match": {
    "lastDescription": {
      "$in": [/^Found a .* at the property$/,/Health Inspection/]
    }
  }}
])

O que nos leva ao fato de que você parece estar procurando o item na matriz com o valor máximo de data. A sintaxe JavaScript deve deixar claro que a abordagem correta aqui é $sort a matriz em "atualizar". Dessa forma, o "primeiro" item na matriz pode ser o "mais recente". E isso é algo que você pode fazer com uma consulta regular.

Para manter o pedido, certifique-se de que novos itens sejam adicionados ao array com $push e $sort assim:
db.getCollection('permits').updateOne(
  { "_id": _idOfDocument },
  {
    "$push": {
      "inspections": {
        "$each": [{ /* Detail of inspection object */ }],
        "$sort": { "inspectionDate": -1 }
      }
    }
  }
)

Na verdade, com um argumento de matriz vazia para $each um updateMany() atualizará todos os seus documentos existentes:
db.getCollection('permits').updateMany(
  { },
  {
    "$push": {
      "inspections": {
        "$each": [],
        "$sort": { "inspectionDate": -1 }
      }
    }
  }
)

Eles realmente só devem ser necessários quando você de fato "alterar" a data armazenada durante as atualizações, e essas atualizações são melhor emitidas com bulkWrite() para efetivamente fazer "ambos" a atualização e a "classificação" do array:
db.getCollection('permits').bulkWrite([
  { "updateOne": {
    "filter": { "_id": _idOfDocument, "inspections._id": indentifierForArrayElement },
    "update": {
      "$set": { "inspections.$.inspectionDate": new Date() }
    }
  }},
  { "updateOne": {
    "filter": { "_id": _idOfDocument },
    "update": {
      "$push": { "inspections": { "$each": [], "$sort": { "inspectionDate": -1 } } }
    }
  }}
])

No entanto, se você nunca "alterou" a data, provavelmente faz mais sentido simplesmente usar o $position modificador e "pré-pend" à matriz em vez de "anexar" e evitando qualquer sobrecarga de um $sort :
db.getCollection('permits').updateOne(
  { "_id": _idOfDocument },
  { 
    "$push": { 
      "inspections": {
        "$each": [{ /* Detail of inspection object */ }],
        "$position": 0
      }
    }
  }
)

Com a matriz permanentemente classificada ou pelo menos construída para que a data "mais recente" seja sempre a "primeira" entrada, você pode simplesmente usar uma expressão de consulta regular:
db.getCollection('permits').find({
  "inspections.0.description": { 
    "$in": [/^Found a .* at the property$/,/Health Inspection/]
  }
})

Portanto, a lição aqui é não tentar forçar expressões calculadas em sua lógica onde você realmente não precisa. Não deve haver nenhuma razão convincente para que você não possa ordenar o conteúdo da matriz como "armazenado" para ter a "data mais recente primeiro " , e mesmo se você achasse que precisava do array em qualquer outra ordem, provavelmente deveria avaliar qual caso de uso é mais importante.

Uma vez reordenado, você pode até tirar vantagem de um índice até certo ponto, desde que as expressões regulares estejam ancoradas no início da string ou pelo menos alguma outra coisa na expressão de consulta faça uma correspondência exata.

Caso você sinta que realmente não pode reordenar o array, então o $onde query é sua única opção atual até que o problema do JIRA seja resolvido. O que esperamos que seja, na verdade, para a versão 4.1, conforme atualmente direcionado, mas isso é mais do que provável de 6 meses a um ano na melhor estimativa.