2017-09-28 4 views
5

J'essaie de compter des valeurs distinctes de champs multiples Par une requête MongoDB Agrégation.Nombre distinct de plusieurs champs utilisant l'agrégation mongodb

Alors, voici mes données:

{ 
    "car_type": "suv", 
    "color": "red", 
    "num_doors": 4 
}, 
{ 
    "car_type": "hatchback", 
    "color": "blue", 
    "num_doors": 4 
}, 
{ 
    "car_type": "wagon", 
    "color": "red", 
    "num_doors": 4 
} 

Je veux un compte distinct de chaque champ:

distinct_count_car_type=3 
distinct_count_color=2 
distinct_count_num_doors=1 

j'ai pu regrouper plusieurs champs, puis faire un compte distinct, mais il ne peut donner moi un compte sur le premier champ. Pas tous. Et c'est aussi un grand ensemble de données.

Répondre

1

Vous cherchez la puissance de ... $objectToArray!

db.foo.aggregate([ 
    {$project: {x: {$objectToArray: "$$CURRENT"}}} 
    ,{$unwind: "$x"} 
    ,{$match: {"x.k": {$ne: "_id"}}} 
    ,{$group: {_id: "$x.k", y: {$addToSet: "$x.v"}}} 
    ,{$addFields: {size: {"$size":"$y"}} } 
        ]); 

Cela donnera:

{ "_id" : "num_doors", "y" : [ 4 ], "size" : 1 } 
{ "_id" : "color", "y" : [ "blue", "red" ], "size" : 2 } 
{ 
    "_id" : "car_type", 
    "y" : [ 
     "wagon", 
     "hatchback", 
     "suv" 
    ], 
    "size" : 3 
} 

Vous pouvez $project ou $addFields comme bon vous semble pour inclure ou exclure l'ensemble des valeurs uniques ou la taille.

+0

Malheureusement j'utilise mongodb 3.2 et $ objectToArray je pense que selon le manuel a été introduit en 3.4. – Deckard

+0

Correct. Je recommande la mise à niveau vers 3.4.4. –

+0

J'ai donc mis à jour et essayé votre solution. Le problème que j'ai maintenant est que les champs dynamiques sur lesquels je voulais compter sont en fait dans un tableau parce qu'ils proviennent d'une recherche $ d'une autre collection. La collection en haut de la question est dans un tableau sur mes résultats en raison d'une recherche $. Donc quand j'essaye de faire un $ objectToArray sur le champ, je reçois "$ objectToArray nécessite une entrée de document, found: array". Et j'ai essayé $ arrayToObject d'abord pour pouvoir ensuite appeler $ objectToArray dessus et je reçois "$ arrayToObject nécessite un objet keys de 'key' et 'v' .Trouvé nombre de clés incorrect: 5". – Deckard

4

Exécution du pipeline global ci-dessous devrait vous donner le résultat souhaité:

db.collection.aggregate([ 
    { 
     "$group": { 
      "_id": null, 
      "distinct_car_types": { "$addToSet": "$car_type" }, 
      "distinct_colors": { "$addToSet": "$color" }, 
      "distinct_num_doors": { "$addToSet": "$num_doors" } 
     } 
    }, 
    { 
     "$project": { 
      "distinct_count_car_type": { "$size": "$distinct_car_types" }, 
      "distinct_count_color": { "$size": "$distinct_colors" }, 
      "distinct_count_num_doors": { "$size": "$distinct_num_doors" } 
     } 
    } 
]) 
+0

nice, et vous pouvez également ajouter '_id: 0' en projection;). –

+0

Cela fonctionne. Merci! Il ralentit un peu mais pas trop. Je pourrais avoir besoin de faire une nouvelle question mais que se passerait-il si ces objets consistaient en un nombre dynamique de champs (ce qui signifie que je ne serais pas en mesure de coder le nom du champ à l'avance). Y a-t-il un moyen d'obtenir des comptes distincts sur un nombre quelconque de champs de ma carte? Comment pourrais-je faire ça? – Deckard

+1

@Deckard voir ci-dessous. –