2017-10-16 11 views
0

Structure des données - un seul document dans une grande collection:MongoDB Chaîne de la propriété commence par requête et Set Group Id

{ 
    OPERATINGSYSTEM: "Android 6.0" 
} 

Problème: Le OperatingSystem peut égaler par exemple "Android 5.0", "Android 6.0", "Windows Phone", "Windows Phone 8.1"

Aucune propriété ne contient uniquement le type de système d'exploitation, par ex. seulement Android.


Je dois connaître le nombre de téléphones Windows et de téléphones Android.

Ma solution temporaire:

db.getCollection('RB').find(
    {OPERATINGSYSTEM: {$regex: "^Android"}} 
).count(); 

que je fais cette requête par le remplacement "^Android" windows phone et ainsi de suite qui prend beaucoup de temps et doit être fait en parallèle.

En utilisant le cadre d'agrégation Je pensais à ce sujet:

db.RB.aggregate(
{$group: {_id: {OPERATINGSYSTEM:"$OPERATINGSYSTEM"}}},) 

Mais en utilisant ce que je reçois une entrée pour chaque version OperatingSystem Android 5.0, Android 6.0 etc ...

La solution que je cherche devrait retourner les données dans ce format:

{ 
    "Android": 50, 
    "Windows Phone": 100 
} 

Comment cela peut-il être fait dans une seule requête?

Répondre

0

Vous pouvez utiliser la réduction de carte et appliquer votre logique dans la fonction de carte.

var map = function(){ 
    var name = this.op.includes("android") ? "Android" : ""; // could be a regexp 

    if(name === ""){ 
     name = this.op.includes("windows") ? "Windows" : ""; 
    } 

    emit(name, 1); 
} 

var reduce = function(key, values){ 
    return Array.sum(values) 
} 

db.operating.mapReduce(map, reduce, {out: "total"}) 

https://docs.mongodb.com/manual/tutorial/map-reduce-examples/

+0

Vous avez raison le cadre d'agrégation n'est pas la meilleure solution dans ce cas. –

2

fourni vos chaînes au moins ont toujours la version numérique comme la dernière chose dans la chaîne, vous pouvez utiliser $split le cadre d'agrégation pour faire un tableau à partir du contenu « espace délimité » , puis retirez le dernier élément du tableau avant la reconstruction:

Compte tenu des données comme:

{ "name" : "Android 6.0" } 
{ "name" : "Android 7.0" } 
{ "name" : "Windows Phone 10" } 

Vous pouvez essayer:

db.getCollection('phones').aggregate([ 
    { "$group": { 
    "_id": { 
     "$let": { 
     "vars": { "split": { "$split": [ "$name", " " ] } }, 
     "in": { 
      "$reduce": { 
      "input": { "$slice": [ "$$split", 0, { "$subtract": [ { "$size": "$$split" }, 1 ] } ] }, 
      "initialValue": "", 
      "in": { 
       "$cond": { 
       "if": { "$eq": [ "$$value", "" ] }, 
       "then": "$$this", 
       "else": { "$concat": [ "$$value", " ", "$$this" ] } 
       } 
      } 
      } 
     } 
     } 
    }, 
    "count": { "$sum": 1 } 
    }}, 
    { "$replaceRoot": { 
    "newRoot": { 
     "$arrayToObject": [[{ "k": "$_id", "v": "$count" }]] 
    } 
    }} 
]) 

C'est possible si votre MongoDB est au moins MongoDB 3.4 pour soutenir à la fois $split et $reduce. Le $replaceRoot est vraiment à propos de nommer les clés, et pas vraiment nécessaire.

Alternativement, vous pouvez utiliser mapReduce:

db.getCollection('phones').mapReduce(
    function() { 
    var re = /\d+/g; 
    emit(this.name.substr(0,this.name.search(re)-1),1); 
    }, 
    function(key,values) { return Array.sum(values) }, 
    { "out": { "inline": 1 } } 
) 

Là où il est plus facile de briser la chaîne par l'indice où une valeur numérique se produit. Dans les deux cas, vous n'êtes pas obligé de "coder" n'importe quoi, et les valeurs des clés dépendent complètement des chaînes dans le contexte.Gardez à l'esprit cependant que, à moins d'un nombre extrêmement élevé de valeurs possibles, les opérations .count() parallèles devraient être les plus rapides à traiter puisque le nombre de curseurs retournés est beaucoup plus rapide que le comptage effectif des entrées agrégées.