Voici un exemple de mes documents:
db.collection(collectionName).updateMany({}, {$set: {"newField": ["$name","$value"] }})
Je souhaite supprimer s'il existe plusieurs documents qui contiennent le même nom et la même valeur. Dans l'exemple ci-dessus, il supprimerait un document, soit {name: "duplicate", value: true, id: 2910921}
ou {name: "duplicate", value: true, id: 32838293}
, peu m'importe lequel.
Jusqu'à présent, j'ai simplement envisagé de créer un nouveau champ pour chacun d'entre eux qui serait quelque chose comme newField: "duplicatetrue" et ensuite je pourrais simplement utiliser distinct sur ceux-ci pour supprimer les dupes, mais j'ai difficulté à comprendre comment concaténer deux champs différents avec des types différents dans un nouveau champ. Je suis également ouvert à de meilleures suggestions. Voici ce que j'ai jusqu'à présent:
[{name:"duplicate", value:true, id:2910921},{name:"duplicate", value:true, id:32838293},{name:"duplicate", value:false, id:3283232},{name:"notDuplicate", value:true, id:382932}]
Cependant, la ligne ci-dessus ne renvoie pas les valeurs, elle affiche plutôt exactement newField: ["$ name", "$ value" ]
Supprimer les guillemets de $ name et $ value ne fonctionne pas non plus.
J'utilise le pilote Node mongodb: 3.5.8
3 Réponses :
Je ne suis pas sûr de mongo mais en utilisant le nœud, vous pouvez supprimer les doublons. J'ai essayé cette méthode pour l'une des exigences qui fonctionnait bien. Veuillez essayer ceci en changeant car vous avez besoin de noms de var.
function arrUnique(arr) { var cleaned = []; arr.forEach(function(itm) { var unique = true; cleaned.forEach(function(itm2) { if (_.isEqual(itm, itm2)) unique = false; }); if (unique) cleaned.push(itm); }); return cleaned; } var newField = arrUnique(newField);
Merci beaucoup, vous m'avez mis à 100% dans la bonne direction en faisant cela dans node et avec l'utilisation d'un foreach, malheureusement, votre fonction n'a pas fonctionné parfaitement pour moi car il semble que cela nécessite du lodash (?) Et je Je crois qu'il a renvoyé un tableau de valeurs uniques plutôt que toutes les différentes valeurs que je pourrais supprimer de la collection.
yah son mon plaisir ici son a donné une idée u. si vous avez de l'aide, donnez aussi un vote positif ;-)
Le faire nativement dans node semblait faire l'affaire (probablement pas le moyen le plus rapide ou le plus efficace mais le suivant a fonctionné):
const array = await db.collection(collectionName).find({}).toArray(); const newArr = array.map((item) => { const newObj = Object.assign({}, item, { hiWorld: `${item.name}${item.amount}`, }); return newObj; }); var uniqueItems = []; var duplicateIds = []; newArr.forEach((item) => { if (uniqueItems.includes(item.hiWorld)) { duplicateIds.push(item._id); } else { uniqueItems.push(item.hiWorld); } }); await db.collection(collectionName).deleteMany({ _id: { $in: duplicateIds } });
Vous pouvez essayer l'agrégation pour obtenir duplicateIds
au lieu de lire tous les documents à coder :-) Même si vous devez simplement utiliser projection
pour lire uniquement certains champs nécessaires.
Vous pouvez le faire de deux manières
$ out
est destructif et que des millions de documents en collection peuvent être un problème dans l'environnement de production, vous pouvez d'abord lire tous les _id
des documents à supprimer et utiliser . deleteMany () pour supprimer tous les documents à la fois. (Vous pouvez utiliser n'importe quel identifiant unique sur un document au lieu de _id
mais j'ai utilisé _id
car il est indexé par défaut - ce qui peut aider à exécuter deleteMany ()
plus rapide). Étape 1:
Utilisation de $ out
- Donc, comme je l'ai dit, c'est destructif car il remplacera l'ensemble collection si le nom d'entrée correspond ou créera une nouvelle collection par le résultat de votre requête d'agrégation. Donc, testez très bien votre requête d'agrégation avant d'utiliser $ out
comme dernière étape. Écrivez également des données dans la collection temporaire et renommez les collections une fois que tout est assez bon. Considérez un temps d'arrêt lors du changement de nom des collections
Requête:
db.collection.deleteMany( { "_id" : {$in : [_ids]} } );
Test: mongoplayground
Étape 2:
_ids
à supprimer de la collection. Requête:
db.collection.aggregate([ /** * Group on matching docs : * { name: "duplicate", value: false}, * { name: "duplicate", value: true}, * { name: "duplicate-yes", value: true}, * { name: "notDuplicate", value: true} * */ { $group: { _id: { name: "$name", value: "$value" }, _idsNeedsToBeDeleted: { $push: "$$ROOT._id" } // push all `_id`'s to an array } }, /** Remove first element - which is removing a doc */ { $project: { _id: 0, _idsNeedsToBeDeleted: { $slice: [ "$_idsNeedsToBeDeleted", 1, { $size: "$_idsNeedsToBeDeleted" } ] } } }, { $unwind: "$_idsNeedsToBeDeleted" // Unwind `_idsNeedsToBeDeleted` }, /** Group without a condition & push all `_idsNeedsToBeDeleted` fields to an array */ { $group: { _id: "", _idsNeedsToBeDeleted: { $push: "$_idsNeedsToBeDeleted" } } }, {$project : { _id : 0 }} // Optional stage /** At the end you'll have an [{ _idsNeedsToBeDeleted: [_ids] }] or [] */ ])
Test: mongoplayground
.deleteMany ()
- supprimez tous les documents: Requête:
db.collection.aggregate([ { $group: { _id: { name: "$name", value: "$value" }, doc: { $last: "$$ROOT" } // Retrieve only last doc in a group } }, { $replaceRoot: { newRoot: "$doc" } // replace doc as object as new root of document }, { $out : 'collection_new' } // Test above aggregation & then use this ])
Considération avant .deleteMany ()
, vous devez vérifier l'agrégation Le résultat n'est pas un tableau vide []
et a un document avec le champ _idsNeedsToBeDeleted
qui est un tableau. De plus, comme nous comparons avec _id
dans la base de données - le tableau des agrégations _idsNeedsToBeDeleted
sera un tableau de chaînes - Donc, itérer sur le tableau, convertir la chaîne en ObjectId () & utiliser ce tableau de
ObjectId ()
dans la requête de suppression.
Remarque:
Indépendamment de ce étape que vous choisissez - Puisque nous regroupons sur nom + valeur
, vous devez vous assurer que tous vos documents contiennent ces champs.
merci, j'apprécie les notes supplémentaires sur les raisons pour lesquelles je choisirais l'une des méthodes de l'autre et les commentaires expliquant chaque étape de l'agrégation, très utiles.
aussi, merci pour l'introduction à mongoplayground, c'est l'outil simple dont je ne savais pas avoir besoin pour tester les pipelines.
quelle est votre version MongoDB?
@whoami mongodb driver: 3.5.8: npmjs.com/package/mongodb , c'est ça tu veux dire?
non, c'est le pilote node.js que vous utilisez pour vous connecter au serveur MongoDB, je demande la version du serveur MongoDB.
mongod - sorties de version: v4.2.3