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.
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});
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
}
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 pymongo , 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
.
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/