56 votes

MongoDB renomme un champ de base de données dans un tableau

Je dois renommer indentifier dans ce :

{ "general" : 
  { "files" : 
    { "file" : 
      [  
        {  "version" : 
          {  "software_program" : "MonkeyPlus",      
             "indentifier" : "6.0.0" 
          } 
        } 
      ] 
    } 
  } 
}

J'ai essayé

db.nrel.component.update(
  {},
  { $rename: {
    "general.files.file.$.version.indentifier" : "general.files.file.$.version.identifier"
  } },
  false, true
)

mais il revient : $rename source may not be dynamic array .

1 votes

$rename ne développe pas les tableaux, doc

0 votes

@Alexander Azarov, des idées pour résoudre ce problème ? J'ai entendu parler de personnes qui copient dans des champs dans lesquels $rename peut aller...

0 votes

Personnellement, j'écris des scripts qui parcourent la collection et font des migrations.

56voto

Eli Gassert Points 9328

Pour ce que ça vaut, même si cela semble horrible à faire, la solution est en fait assez simple. Cela dépend bien sûr du nombre d'enregistrements que vous avez. Mais voici mon exemple :

db.Setting.find({ 'Value.Tiers.0.AssetsUnderManagement': { $exists: 1 } }).snapshot().forEach(function(item)
{    
    for(i = 0; i != item.Value.Tiers.length; ++i)
    {
        item.Value.Tiers[i].Aum = item.Value.Tiers[i].AssetsUnderManagement;
        delete item.Value.Tiers[i].AssetsUnderManagement;
    }

    db.Setting.update({_id: item._id}, item);
});

J'itère sur ma collection où le tableau est trouvé et le "mauvais" nom est trouvé. Je fais ensuite une itération sur la sous-collection, je fixe la nouvelle valeur, je supprime l'ancienne et je mets à jour l'ensemble du document. C'était relativement facile. Il est vrai que je n'ai que quelques dizaines de milliers de lignes à parcourir, dont seulement quelques dizaines répondent aux critères.

J'espère quand même que cette réponse aidera quelqu'un !

Edit : Ajouté snapshot() à la requête. Voir pourquoi dans les commentaires.

Vous devez appliquer snapshot() au curseur avant de récupérer les documents dans la base de données. Vous ne pouvez utiliser snapshot() avec des collections non triées.

De MongoDB 3.4, snapshot() a été supprimée. Donc, si vous utilisez Mongo 3.4+ ,l'exemple ci-dessus devrait supprimer snapshot() fonction.

26voto

Remon van Vliet Points 10795

Comme indiqué dans la documentation, il n'existe aucun moyen de renommer directement les champs d'un tableau avec une seule commande. Votre seule option est d'itérer sur les documents de votre collection, de les lire et de mettre à jour chacun d'entre eux avec la commande $unset vieux/ $set nouvelles opérations.

37 votes

Coincé avec MongoDB... des choses comme ça me rendent triste :(

0 votes

Je ne suis pas en désaccord. C'est un fruit relativement facile à cueillir, j'imagine.

0 votes

Cette réponse est dépassée, voir la réponse de Sudhesh qui est la meilleure.

21voto

fraccy Points 235

J'ai eu un problème similaire. Dans ma situation, j'ai trouvé que la méthode suivante était beaucoup plus facile :

  1. J'ai exporté la collection en json :

    mongoexport --db mydb --collection modules --out modules.json

  2. J'ai fait une recherche et un remplacement sur le json en utilisant mon utilitaire d'édition de texte favori.

  3. J'ai réimporté le fichier édité, en laissant tomber l'ancienne collection en cours de route :

    mongoimport --db mydb --collection modules --drop --file modules.json

3 votes

Je suis un peu sceptique quant à l'efficacité de cette approche sur un grand ensemble de données. Si ma base de données fait 5 Go, ce processus semble lent. Ou peut-être que je n'utilise pas les bons outils d'édition de texte =-}

3 votes

Méfiez-vous également des bases de données en direct. Si des enregistrements sont ajoutés/modifiés pendant le temps nécessaire pour effectuer ces étapes, les changements seront perdus.

0 votes

Cela ne pose pas de problème si vous avez quelque chose qui ne fonctionne que sur votre ordinateur portable, mais ce n'est pas une solution viable si vous travaillez dans un environnement de production.

20voto

Xavier Guihot Points 6414

Démarrage Mongo 4.2 , db.collection.update() peut accepter un pipeline d'agrégation, permettant finalement la mise à jour d'un champ basé sur sa propre valeur :

// { general: { files: { file: [
//   { version: { software_program: "MonkeyPlus", indentifier: "6.0.0" } }
// ] } } }
db.collection.updateMany(
  {},
  [{ $set: { "general.files.file": {
       $map: {
         input: "$general.files.file",
         as: "file",
         in: {
           version: {
             software_program: "$$file.version.software_program",
             identifier: "$$file.version.indentifier" // fixing the typo here
           }
         }
       }
  }}}]
)
// { general: { files: { file: [
//   { version: { software_program: "MonkeyPlus", identifier: "6.0.0" } }
// ] } } }

Littéralement, cette update s documents par (re) $set ting le "general.files.file" par $map ping son "file" éléments in a "version" contenant le même "software_program" et le champ renommé "identifier" qui contient ce qui était auparavant la valeur du champ "indentifier" .


Quelques détails supplémentaires :

  • La première partie {} est la requête de correspondance, qui filtre les documents à mettre à jour (dans ce cas, tous les documents).

  • La deuxième partie [{ $set: { "general.files.file": { ... }}}] est le pipeline d'agrégation des mises à jour (notez les crochets qui indiquent l'utilisation d'un pipeline d'agrégation) :

    • $set est un nouvel opérateur d'agrégation qui, dans ce cas, remplace la valeur de l'élément "general.files.file" le tableau.
    • Utilisation d'un $map nous remplaçons tous les éléments de l'opération "general.files.file" par les mêmes éléments, mais avec une valeur de "identifier" plutôt que "indentifier" :
    • input est le tableau à mettre en correspondance.
    • as est le nom de variable donné aux éléments bouclés
    • in est la transformation réelle appliquée aux éléments. Dans ce cas, elle remplace les éléments par un fichier "version" composé d'un objet "software_program" et un "identifier" champs. Ces champs sont remplis en extrayant leurs valeurs précédentes à l'aide de la fonction $$file.xxxx (où file est le nom donné aux éléments de la as partie).

10voto

J'ai été confronté à ce problème avec le même schéma. Donc cette requête sera utile pour quelqu'un qui veut renommer le champ dans un tableau incorporé.

db.getCollection("sampledocument").updateMany({}, [
  {
    $set: {
      "general.files.file": {
        $map: {
          input: "$general.files.file",
          in: {
            version: {
              $mergeObjects: [
                "$$this.version",
                { identifer: "$$this.version.indentifier" },
              ],
            },
          },
        },
      },
    },
  },
  { $unset: "general.files.file.version.indentifier" },
]);

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