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

Contar campo de subdocumento distinto e saída como chaves nomeadas


O caso básico aqui é usar .aggregate() com $unwind porque você precisa acessar os valores na matriz como suas chaves de agrupamento e, claro, $group porque é assim que você "agrupa" as coisas:
db.collection.aggregate([
  { "$match": { "auctionId": 22 } },
  { "$unwind": "$itmLst" },
  { "$group": {
    "_id": "$itmLst.category",
    "count": { "$sum": 1 }
  }}
])

Isso lhe dará uma saída como:
{ "_id": "ANTIQUES", "count": 56 }
{ "_id": "TOOLS", "count": 89 }
{ "_id": "JEWLRY", "count":  45 }

Agora você realmente deve aprender a conviver com isso, porque uma "lista" no formato de cursor padrão é uma coisa boa que é naturalmente iterável. Além disso, as chaves nomeadas IMHO não se prestam naturalmente à apresentação de dados e geralmente você deseja uma propriedade comum em uma lista iterável.

Se você realmente pretende usar a saída de chaves nomeadas singulares, precisará do MongoDB 3.4.4 ou superior para ter acesso a $arrayToObject que permitirá que você use os valores como os nomes das chaves e, claro, $replaceRoot para usar essa saída de expressão como o novo documento para produzir:
db.collection.aggregate([
  { "$match": { "auctionId": 22 } },
  { "$unwind": "$itmLst" },
  { "$group": {
    "_id": "$itmLst.category",
    "count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": null,
    "data": { "$push": { "k": "$_id", "v": "$count" } }
  }},
  { "$replaceRoot": {
    "newRoot": {
      "$arrayToObject": "$data"
    }
  }}
])

Ou, se você não tiver essa opção, deverá converter a saída do cursor em código:
db.collection.aggregate([
  { "$match": { "auctionId": 22 } },
  { "$unwind": "$itmLst" },
  { "$group": {
    "_id": "$itmLst.category",
    "count": { "$sum": 1 }
  }}
]).toArray().reduce((acc,curr) => 
  Object.assign(acc,{ [curr._id]: curr.count }),
  {}
)

Ambos se fundem em um único objeto com chaves nomeadas da saída de agregação original:
{
    "ANTIQUES": 56,
    "TOOLS": 89,
    "JEWLRY": 45,
    ...
}

E isso mostra que o resultado da saída original foi realmente suficiente e que normalmente você deseja que esse tipo de "remodelagem final" seja feito no código que usa a saída do cursor, se você realmente precisar dessa reformulação desde o básico os dados necessários foram retornados de qualquer maneira.