mongodb número de contagem de valores distintos por campo / chave

104

Existe uma consulta para calcular quantos valores distintos um campo contém no banco de dados.

fe Eu tenho um campo para país e existem 8 tipos de valores de país (espanha, inglaterra, frança, etc ...)

Se alguém adicionar mais documentos com um novo país, gostaria que a consulta retornasse 9.

Existe uma maneira mais fácil de agrupar e contar?

Liatz
fonte
2
Você já olhou para a estrutura de agregação ?
WiredPrairie de
1
Ou reduzir o mapa ?
WiredPrairie de

Respostas:

198

MongoDB tem um distinctcomando que retorna uma matriz de valores distintos para um campo; você pode verificar o comprimento da matriz para uma contagem.

Também existe um db.collection.distinct()auxiliar de shell :

> db.countries.distinct('country');
[ "Spain", "England", "France", "Australia" ]

> db.countries.distinct('country').length
4
Stennie
fonte
47
isso realmente não funciona se o seu número de valores distintos for muito alto ... se você estiver olhando para nomes distintos de pessoas no mundo ou algo assim. você tem uma resposta que escala?
underrun
3
1+ para comprimento. eu estava lutando para encontrar algo assim. Obrigado.
Adeel Ahmad
Não sei por que eles não usam count () lá também
Marian Klühspies
1
@MarianKlühspies - porque é apenas um array de javascript, que usa a propriedade length para contar o número de elementos.
UpTheCreek
Exatamente o que eu estava procurando ... TY
Maulzey
113

Aqui está um exemplo de uso da API de agregação. Para complicar o caso, estamos agrupando por palavras que não diferenciam maiúsculas de minúsculas da propriedade de array do documento.

db.articles.aggregate([
    {
        $match: {
            keywords: { $not: {$size: 0} }
        }
    },
    { $unwind: "$keywords" },
    {
        $group: {
            _id: {$toLower: '$keywords'},
            count: { $sum: 1 }
        }
    },
    {
        $match: {
            count: { $gte: 2 }
        }
    },
    { $sort : { count : -1} },
    { $limit : 100 }
]);

que dão resultados como

{ "_id" : "inflammation", "count" : 765 }
{ "_id" : "obesity", "count" : 641 }
{ "_id" : "epidemiology", "count" : 617 }
{ "_id" : "cancer", "count" : 604 }
{ "_id" : "breast cancer", "count" : 596 }
{ "_id" : "apoptosis", "count" : 570 }
{ "_id" : "children", "count" : 487 }
{ "_id" : "depression", "count" : 474 }
{ "_id" : "hiv", "count" : 468 }
{ "_id" : "prognosis", "count" : 428 }
especialista
fonte
2
Logado apenas para + esta resposta. Obrigado! A propósito, se você estiver fazendo isso em um campo exclusivo, apenas remova a linha de desenrolamento.
Richie Rich
@RichieRich, unwindé necessário porque o código está agrupando valores individuais de um campo de array que corresponde a como distinctfunciona.
Paulo
@Paul, o que Richie disse é que se o agrupamento for feito apenas no campo "normal" (string, int etc.), você não precisa da etapa de desenrolamento. Não é correto?
Guyarad
@guyarad unwindé necessário ao trabalhar com matrizes.
Paul,
+1 para a resposta, exatamente o que eu estava trabalhando, por mais distinto que tenha seus próprios encantos, mas isso é apenas ouro :) - de qualquer forma, tenho que ler mais sobre agregados para alcançar o conjunto desejado de resultados para filtrar dados
Talha
21

Com o MongoDb 3.4.4 e mais recente, você pode aproveitar o uso de um $arrayToObjectoperador e um $replaceRootpipeline para obter as contagens.

Por exemplo, suponha que você tenha uma coleção de usuários com funções diferentes e gostaria de calcular as contagens distintas das funções. Você precisaria executar o seguinte pipeline agregado:

db.users.aggregate([
    { "$group": {
        "_id": { "$toLower": "$role" },
        "count": { "$sum": 1 }
    } },
    { "$group": {
        "_id": null,
        "counts": {
            "$push": { "k": "$_id", "v": "$count" }
        }
    } },
    { "$replaceRoot": {
        "newRoot": { "$arrayToObject": "$counts" }
    } }    
])

Exemplo de saída

{
    "user" : 67,
    "superuser" : 5,
    "admin" : 4,
    "moderator" : 12
}
chridam
fonte
Esta não é a resposta à pergunta, mas é útil mesmo assim. Eu me pergunto como isso funciona em comparação com .distinct().
Redsandro
9

Você pode aproveitar as extensões do Mongo Shell . É uma única importação .js que você pode anexar ao seu $HOME/.mongorc.js, ou programaticamente, se estiver codificando em Node.js / io.js também.

Amostra

Para cada valor distinto de campo conta as ocorrências em documentos opcionalmente filtrados por consulta

> db.users.distinctAndCount('name', {name: /^a/i})

{
  "Abagail": 1,
  "Abbey": 3,
  "Abbie": 1,
  ...
}

O parâmetro de campo pode ser uma matriz de campos

> db.users.distinctAndCount(['name','job'], {name: /^a/i})

{
  "Austin,Educator" : 1,
  "Aurelia,Educator" : 1,
  "Augustine,Carpenter" : 1,
  ...
}
Evandrix
fonte
como eu importaria isso no nó?
Salmaan P
require("./script.js"), suponho
evandrix
certo, mas não consegui inserir as funções. Como faço para usá-los. Eles são definidos como db.protoptype.distinctAndCount
Salmaan P
Há uma seção de instruções no readme do repo (RTFM! 1 !! 1!) Basicamente, coloque o .mongorc.jsarquivo em seu diretório inicial. Feito.
Janis F
6

Para encontrar distintos na field_1coleção, mas também queremos alguma WHEREcondição do que podemos fazer como segue:

db.your_collection_name.distinct('field_1', {WHERE condition here and it should return a document})

Portanto, encontre um número diferente namesde uma coleção em que idade> 25 será como:

db.your_collection_name.distinct('names', {'age': {"$gt": 25}})

Espero que ajude!

Vimal
fonte