74 votes

MongoDB: Mise à jour de documents à l'aide de données du même document

J'ai une liste de documents, chacun avec des propriétés lat et long (entre autres).

 { 'lat': 1, 'lon': 2, someotherdata [...] } 
{ 'lat': 4, 'lon': 1, someotherdata [...] }
[...]
 

Je veux le modifier pour qu'il ressemble à ceci:

 { 'coords': {'lat': 1, 'lon': 2}, someotherdata [...]} 
{ 'coords': {'lat': 4, 'lon': 1}, someotherdata [...]}
[...]
 

Jusqu'ici j'ai ceci:

 db.events.update({}, {$set : {'coords': {'lat': db.events.lat, 'lon': db.events.lon}}}, false, true)
 

Mais il traite db.events.lat et db.events.lon comme des chaînes. Comment puis-je référencer les propriétés du document?

À votre santé.

205voto

Niels van der Rest Points 11802

Mise à jour: Si tout ce que vous avez à faire est de changer la structure d'un document sans modifier les valeurs, voir gipset de réponse pour une solution sympa.


Selon une (actuellement non disponible) des commentaires sur la documentation de mise à Jour de la page, vous ne pouvez pas référencer le document en cours de propriétés à partir d'une update().

Vous aurez à parcourir tous les documents et de les mettre à jour comme ceci:

db.events.find().snapshot().forEach(
  function (e) {
    // update document, using its own properties
    e.coords = { lat: e.lat, lon: e.lon };

    // remove old properties
    delete e.lat;
    delete e.lon;

    // save the updated document
    db.events.save(e);
  }
)

Une telle fonction peut également être utilisé dans une carte-réduire de travail ou un serveur-side db.eval() de l'emploi, en fonction de vos besoins.

55voto

gipset Points 314

L'opérateur $ rename (introduit un mois après la publication de cette question) facilite grandement l'exécution de ce type de tâches sans qu'il soit nécessaire de modifier les valeurs.

Insérer des documents de test

 db.events.insert({ 'lat': 1, 'lon': 2, someotherdata: [] })
db.events.insert({ 'lat': 4, 'lon': 1, someotherdata: [] })
 

utiliser l'opérateur $rename

 db.events.update({}, {$rename: {'lat': 'coords.lat', 'lon': 'coords.lon'}}, false, true)
 

Résultats

 db.events.find()
{
    "_id" : ObjectId("5113c82dd28c4e8b79971add"),
    "coords" : {
        "lat" : 1,
        "lon" : 2
    },
    "someotherdata" : [ ]
}
{
    "_id" : ObjectId("5113c82ed28c4e8b79971ade"),
    "coords" : {
        "lat" : 4,
        "lon" : 1
    },
    "someotherdata" : [ ]
}
 

3voto

Adam Comerford Points 9084

Tant que vous êtes OK avec la création d'une copie des données, l'agrégation cadre peut être utilisé comme une alternative ici. Vous avez également la possibilité de faire plus de données si vous souhaitez utiliser d'autres opérateurs, mais le seul que vous avez besoin est - $project. C'est un peu du gaspillage en termes d'espace, mais peut être plus rapide et plus appropriée pour certaines utilisations. Pour illustrer, je vais d'abord insérer des données de l'échantillon en foo collection:

db.foo.insert({ 'lat': 1, 'lon': 2, someotherdata : [1, 2, 3] }) db.foo.insert({ 'lat': 4, 'lon': 1, someotherdata : [4, 5, 6] })

Maintenant, nous utiliserons $project de retravailler l' lat et lon champs, puis les envoyer à l' newfoo collection:

db.foo.aggregate([
    {$project : {_id : "$_id", "coords.lat" : "$lat", "coords.lon" : "$lon", "someotherdata" : "$someotherdata" }},
    { $out : "newfoo" }
])

Ensuite, vérifiez newfoo de nos données modifiées:

db.newfoo.find()
{ "_id" : ObjectId("544548a71b5cf91c4893eb9a"), "someotherdata" : [ 1, 2, 3 ], "coords" : { "lat" : 1, "lon" : 2 } }
{ "_id" : ObjectId("544548a81b5cf91c4893eb9b"), "someotherdata" : [ 4, 5, 6 ], "coords" : { "lat" : 4, "lon" : 1 } }

Une fois que vous êtes heureux avec les nouvelles données, vous pouvez alors utiliser l' renameCollection() commande à abandonner les anciennes données et utiliser les nouvelles données en vertu de l'ancien nom de:

> db.newfoo.renameCollection("foo", true)
{ "ok" : 1 }
> db.foo.find()
{ "_id" : ObjectId("544548a71b5cf91c4893eb9a"), "someotherdata" : [ 1, 2, 3 ], "coords" : { "lat" : 1, "lon" : 2 } }
{ "_id" : ObjectId("544548a81b5cf91c4893eb9b"), "someotherdata" : [ 4, 5, 6 ], "coords" : { "lat" : 4, "lon" : 1 } }

Une dernière note, - jusqu'à ce que le SERVEUR 7944 est terminée, vous ne pouvez pas faire l'équivalent d'un instantané en faisant allusion à l' _id de l'indice comme le suggère cette réponse et ainsi, vous pouvez finir par frapper un document de plus d'une fois si l'activité de ailleurs fait déplacer. Depuis que vous êtes à l'insertion de l' _id champ dans cet exemple, une telle éventualité serait la cause d'une clé unique de la violation, de sorte que vous n'aurez donc pas à dupes, mais vous pourriez avoir une "vieille" version d'un document. Comme toujours, vérifiez vos données à fond avant de le laisser tomber, et, de préférence, prendre une copie de sauvegarde.

0voto

luckytaxi Points 2917

De la CLI? Je pense que vous devez d'abord extraire les valeurs et les affecter à une variable. Ensuite, lancez votre commande de mise à jour.

Ou (je n'ai pas essayé), supprimez 'db' de la chaîne. events.lat et events.lon Si cela fonctionne, vous aurez toujours plusieurs valeurs, les anciennes valeurs pour "lat" et "lon" et le nouveau tableau que vous avez créé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X