Trouver tous les documents en double dans une collection MongoDB par un champ Clé

supposez que j'ai une collection avec un ensemble de documents. quelque chose comme cela.

{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":1, "name" : "foo"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":2, "name" : "bar"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":3, "name" : "baz"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":4, "name" : "foo"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":5, "name" : "bar"}
{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":6, "name" : "bar"}

je veux trouver toutes les entrées dupliquées dans cette collection par le champ "nom". Par exemple," foo "apparaît deux fois et" bar " apparaît trois fois.

48
demandé sur ekad 2012-02-29 04:23:37

4 réponses

Note: Cette solution est la plus facile à comprendre, mais pas la meilleure.

Vous pouvez utiliser mapReduce pour savoir combien de fois un document contient un certain champ:

var map = function(){
   if(this.name) {
        emit(this.name, 1);
   }
}

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

var res = db.collection.mapReduce(map, reduce, {out:{ inline : 1}});
db[res.result].find({value: {$gt: 1}}).sort({value: -1});
17
répondu ggreiner 2015-10-15 22:28:40

la réponse acceptée est terriblement lente sur les grandes collections, et ne renvoie pas le _id s des enregistrements en double.

L'agrégation est beaucoup plus rapide et peut retourner le _id s:

db.collection.aggregate([
  { $group: {
    _id: { name: "$name" },   // replace `name` here twice
    uniqueIds: { $addToSet: "$_id" },
    count: { $sum: 1 } 
  } }, 
  { $match: { 
    count: { $gte: 2 } 
  } },
  { $sort : { count : -1} },
  { $limit : 10 }
]);

dans la première étape du pipeline d'agrégation, le $group l'exploitant agrège les documents par le champ name et stocke par le champ uniqueIds chaque valeur _id des enregistrements groupés. Le $sum l'opérateur additionne les valeurs des champs qui lui sont passés, dans ce cas la constante 1 - comptant ainsi le nombre d'enregistrements groupés dans le champ count .

dans la deuxième étape du pipeline, nous utilisons $ match filtrer les documents avec un count d'au moins 2, c'est-à-dire des duplicata.

ensuite, nous trions les doublons les plus fréquents en premier, et limitons les résultats à la top 10.

cette requête produira jusqu'à $limit des enregistrements avec des noms en double, ainsi que leurs _id s. Par exemple:

{
  "_id" : {
    "name" : "Toothpick"
},
  "uniqueIds" : [
    "xzuzJd2qatfJCSvkN",
    "9bpewBsKbrGBQexv4",
    "fi3Gscg9M64BQdArv",
  ],
  "count" : 3
},
{
  "_id" : {
    "name" : "Broom"
  },
  "uniqueIds" : [
    "3vwny3YEj2qBsmmhA",
    "gJeWGcuX6Wk69oFYD"
  ],
  "count" : 2
}
138
répondu expert 2014-06-06 20:50:02

pour une solution générique de Mongo, voir la MongoDB cookbook recipe for finding duplicates using group . Notez que l'agrégation est plus rapide et plus puissante en ce sens qu'elle peut retourner les _id des enregistrements dupliqués.

pour , la réponse acceptée (en utilisant mapReduce) n'est pas si efficace. Au lieu de cela, nous pouvons utiliser le groupe méthode:

$connection = 'mongodb://localhost:27017';
$con        = new Mongo($connection); // mongo db connection

$db         = $con->test; // database 
$collection = $db->prb; // table

$keys       = array("name" => 1); Select name field, group by it

// set intial values
$initial    = array("count" => 0);

// JavaScript function to perform
$reduce     = "function (obj, prev) { prev.count++; }";

$g          = $collection->group($keys, $initial, $reduce);

echo "<pre>";
print_r($g);

sortie sera celle-ci :

Array
(
    [retval] => Array
        (
            [0] => Array
                (
                    [name] => 
                    [count] => 1
                )

            [1] => Array
                (
                    [name] => MongoDB
                    [count] => 2
                )

        )

    [count] => 3
    [keys] => 2
    [ok] => 1
)

la requête SQL équivalente serait: SELECT name, COUNT(name) FROM prb GROUP BY name . Notez que nous devons toujours filtrer les éléments avec un nombre de 0 à partir du tableau. Encore une fois, reportez-vous à la MongoDB cookbook recette pour trouver des doublons en utilisant group pour la solution canonique en utilisant group .

5
répondu Prasanth Bendra 2014-04-24 06:38:43

j'ai trouvé des informations utiles sur le blog officiel de mongo lab: http://blog.mongolab.com/2014/03/finding-duplicate-keys-with-the-mongodb-aggregation-framework/

2
répondu Krunal Shah 2015-10-14 08:38:26