web-dev-qa-db-fra.com

mongodb obtient des enregistrements distincts

J'utilise mongoDB dans lequel j'ai une collection de format suivant.

{"id" : 1 , name : x  ttm : 23 , val : 5 }
{"id" : 1 , name : x  ttm : 34 , val : 1 }
{"id" : 1 , name : x  ttm : 24 , val : 2 }
{"id" : 2 , name : x  ttm : 56 , val : 3 }
{"id" : 2 , name : x  ttm : 76 , val : 3 }
{"id" : 3 , name : x  ttm : 54 , val : 7 }

Sur cette collection, j'ai demandé à obtenir des enregistrements dans l'ordre décroissant comme ceci:

db.foo.find({"id" : {"$in" : [1,2,3]}}).sort(ttm : -1).limit(3)

Mais cela donne deux enregistrements du même id = 1 et je veux des enregistrements tels que cela donne 1 enregistrement par id.

Est-ce possible dans mongodb?

23
Swapnil Sonawane

Il existe une commande distinct dans mongodb, qui peut être utilisée conjointement avec une requête. Cependant, je crois que cela renvoie simplement une liste distincte de valeurs pour une clé spécifique que vous nommez (c'est-à-dire dans votre cas, vous ne recevrez que les valeurs id), donc je ne suis pas sûr que cela vous donnera exactement ce que vous voulez si vous besoin de tous les documents - vous pourriez avoir besoin de MapReduce à la place.

Documentation sur distinct: http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Distinct

26
AdaTheDev

Vous souhaitez utiliser l'agrégation. Vous pouvez faire ça comme ceci:

db.test.aggregate([
    // each Object is an aggregation.
    {
        $group: {
            originalId: {$first: '$_id'}, // Hold onto original ID.
            _id: '$id', // Set the unique identifier
            val:  {$first: '$val'},
            name: {$first: '$name'},
            ttm:  {$first: '$ttm'}
        }

    }, {
        // this receives the output from the first aggregation.
        // So the (originally) non-unique 'id' field is now
        // present as the _id field. We want to rename it.
        $project:{
            _id : '$originalId', // Restore original ID.

            id  : '$_id', // 
            val : '$val',
            name: '$name',
            ttm : '$ttm'
        }
    }
])

Ce sera très rapide ... ~ 90ms pour ma base de données de test de 100 000 documents.

Exemple:

db.test.find()
// { "_id" : ObjectId("55fb595b241fee91ac4cd881"), "id" : 1, "name" : "x", "ttm" : 23, "val" : 5 }
// { "_id" : ObjectId("55fb596d241fee91ac4cd882"), "id" : 1, "name" : "x", "ttm" : 34, "val" : 1 }
// { "_id" : ObjectId("55fb59c8241fee91ac4cd883"), "id" : 1, "name" : "x", "ttm" : 24, "val" : 2 }
// { "_id" : ObjectId("55fb59d9241fee91ac4cd884"), "id" : 2, "name" : "x", "ttm" : 56, "val" : 3 }
// { "_id" : ObjectId("55fb59e7241fee91ac4cd885"), "id" : 2, "name" : "x", "ttm" : 76, "val" : 3 }
// { "_id" : ObjectId("55fb59f9241fee91ac4cd886"), "id" : 3, "name" : "x", "ttm" : 54, "val" : 7 }


db.test.aggregate(/* from first code snippet */)

// output
{
    "result" : [
        {
            "_id" : ObjectId("55fb59f9241fee91ac4cd886"),
            "val" : 7,
            "name" : "x",
            "ttm" : 54,
            "id" : 3
        },
        {
            "_id" : ObjectId("55fb59d9241fee91ac4cd884"),
            "val" : 3,
            "name" : "x",
            "ttm" : 56,
            "id" : 2
        },
        {
            "_id" : ObjectId("55fb595b241fee91ac4cd881"),
            "val" : 5,
            "name" : "x",
            "ttm" : 23,
            "id" : 1
        }
    ],
    "ok" : 1
}

AVANTAGES: C'est certainement la méthode la plus rapide.

CONTRE: implique l'utilisation de l'API d'agrégation compliquée. En outre, il est étroitement lié au schéma d'origine du document. Cependant, il peut être possible de généraliser cela.

16
robert

Le problème est que vous souhaitez distiller 3 enregistrements correspondants jusqu'à un sans fournir de logique dans la requête pour savoir comment choisir entre les résultats correspondants.

Vos options sont essentiellement de spécifier une logique d'agrégation d'une certaine sorte (sélectionnez la valeur max ou min pour chaque colonne, par exemple), ou d'exécuter une requête distincte de sélection et de sélectionner uniquement les champs que vous souhaitez être distincts.

querymongo.com fait un bon travail de traduction de ces requêtes distinctes pour vous (de SQL à MongoDB).

Par exemple, ce SQL:

SELECT DISTINCT columnA FROM collection WHERE columnA > 5

Est renvoyé comme ce MongoDB:

db.runCommand({
    "distinct": "collection",
    "query": {
        "columnA": {
            "$gt": 5
        }
    },
    "key": "columnA"
});
7
robertjmoore

Je pense que vous pouvez utiliser des agrégats comme celui-ci

collection.aggregate({
   $group : {
        "_id" : "$id",
        "docs" : { 
            $first : { 
            "name" : "$name",
            "ttm" : "$ttm",
            "val" : "$val",
            }
        } 
    }
});
7
Sajjad Ashraf

Si vous voulez écrire le résultat distinct dans un fichier en utilisant javascript ... c'est comme ça que vous faites

cursor = db.myColl.find({'fieldName':'fieldValue'})

var Arr = new Array();
var count = 0;

cursor.forEach(

function(x) {

    var temp = x.id;    
var index = Arr.indexOf(temp);      
if(index==-1)
   {
     printjson(x.id);
     Arr[count] = temp;
         count++;
   }
})
2
rajibdotnet