Comment changer le type d'un champ?

159

J'essaie de changer le type d'un champ à partir de la coque mongo.

Je fais ça ...

db.meta.update(
  {'fields.properties.default': { $type : 1 }}, 
  {'fields.properties.default': { $type : 2 }}
)

Mais ça ne marche pas!

J. Quintas
la source
Si quelqu'un se trouve dans la même situation que moi, devant toStringun champ de document, voici le petit programme que j'ai créé / utilisé .
talles le

Réponses:

214

La seule façon de modifier le $typedes données est d'effectuer une mise à jour sur les données où les données ont le type correct.

Dans ce cas, il semble que vous essayez de changer le $type de 1 (double) à 2 (chaîne) .

Chargez simplement le document à partir de la base de données, effectuez le cast ( new String(x)), puis enregistrez à nouveau le document.

Si vous devez le faire par programme et entièrement à partir du shell, vous pouvez utiliser la find(...).forEach(function(x) {})syntaxe.


En réponse au deuxième commentaire ci-dessous. Remplacez le champ badpar un nombre par une chaîne dans la collection foo.

db.foo.find( { 'bad' : { $type : 1 } } ).forEach( function (x) {   
  x.bad = new String(x.bad); // convert field to string
  db.foo.save(x);
});
Vice-président Gates
la source
1
Une chance d'un exemple - changer un type de champ de int en chaîne (ou vice versa), à partir du shell?
Alister Bulman
30
dans le cas où Int32-> String, new String(x.bad)crée une collection de chaînes avec une x.badvaleur d' élément d'index 0 . Variante ""+x.bad, décrite par Simone, fonctionne comme vous le souhaitez - crée une valeur de chaîne au lieu de Int32
Dao
Le code ci-dessus convertit les données de champ de double en tableau au lieu de double en chaîne. Mes données réelles étaient dans ce format: 3.1 alors que le code simone fonctionne bien pour moi
Pankaj Khurana
2
Il y avait une situation où j'avais besoin de convertir le champ _id et de ne pas entrer en conflit avec d'autres index:db.questions.find({_id:{$type:16}}).forEach( function (x) { db.questions.remove({_id:x._id},true); x._id = ""+x._id; db.questions.save(x); });
Matt Molnar
@SundarBons oui vous réécrivez un champ dans votre base de données, c'est un gros problème, peu importe comment vous le faites. Si vous utilisiez SQL et que c'était une grande table, vous devrez probablement prendre un certain temps d'arrêt.
Gates VP
161

Convertir le champ String en Integer:

db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) { 
    obj.field-name = new NumberInt(obj.field-name);
    db.db-name.save(obj);
});

Convertir le champ entier en chaîne:

db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) {
    obj.field-name = "" + obj.field-name;
    db.db-name.save(obj);
});
Simone
la source
C'est génial - savez-vous comment convertir une chaîne (pensez à une devise comme «1.23») en entier 123? Je suppose que vous devrez l'analyser sous forme de virgule flottante ou décimale, de le multiplier par 100, puis de l'enregistrer en tant qu'entier, mais je ne trouve pas les bons documents pour le faire. Merci!
Brian Armstrong
En fait, ce travail est bon. Mais j'ai une application fonctionnant avec mongoid 2.4.0-stable qui a des champs tels que field: customer_count, type: Integer & une validation comme validates_numericality_of: customer_count qui fonctionnait bien. Maintenant, lorsque je passe à mongoid vers 3.0.16, lorsque j'attribue une valeur de chaîne, il la convertit automatiquement en 0. sans erreur. Je veux lancer une erreur sur une mauvaise affectation de données, ce comportement me semble étrange.
Swapnil Chincholkar
4
J'ai couru ceci et j'ai eu l'erreur: Erreur: impossible de convertir la chaîne en entier (shell): 1
Mittenchops
Et si vous avez besoin de convertir une chaîne (ou un entier 32 bits "normal") en entier 64 bits, utilisez NumberLongcomme ici:db.db-name.find({field-name : {$exists : true}}).forEach( function(obj) { obj.field-name = new NumberLong(obj.field-name); db.db-name.save(obj); } );
boryn
ça marche bien. Puis-je savoir comment changer le type de données dans les champs de document incorporés
Sankar Muniyappa
43

Pour la conversion de chaîne en int.

db.my_collection.find().forEach( function(obj) {
    obj.my_value= new NumberInt(obj.my_value);
    db.my_collection.save(obj);
});

Pour la conversion de chaîne en double.

    obj.my_value= parseInt(obj.my_value, 10);

Pour le flotteur:

    obj.my_value= parseFloat(obj.my_value);
David Dehghan
la source
2
Je recommanderais également de spécifier le radix- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Russ Cam
5
Attention , j'ai testé cela avec Robomongo, et cela a abouti au type 1: double. Dû utilisernew NumberInt()
Daniel F
Daniel, votre solution n'est pas correcte ... la solution de David est correcte
Rajib
22
db.coll.find().forEach(function(data) {
    db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})
Russell
la source
15

Au départ Mongo 4.2, db.collection.update()peut accepter un pipeline d'agrégation, permettant enfin la mise à jour d'un champ en fonction de sa propre valeur:

// { a: "45", b: "x" }
// { a:  53,  b: "y" }
db.collection.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $toString: "$a" } } }],
  { multi: true }
)
// { a: "45", b: "x" }
// { a: "53", b: "y" }
  • La première partie { a : { $type: 1 } }est la requête de correspondance:

    • Il filtre les documents à mettre à jour.
    • Dans ce cas, puisque nous voulons convertir "a"en chaîne lorsque sa valeur est un double, cela correspond aux éléments pour lesquels "a"est de type 1(double)).
    • Ce tableau fournit les codes représentant les différents types possibles.
  • La deuxième partie [{ $set: { a: { $toString: "$a" } } }]est le pipeline d'agrégation de mise à jour:

    • Notez les crochets carrés indiquant que cette requête de mise à jour utilise un pipeline d'agrégation.
    • $setest un nouvel opérateur d'agrégation ( Mongo 4.2) qui dans ce cas modifie un champ.
    • Cela peut être simplement lu comme "$set"la valeur de "a"à "$a"converti "$toString".
    • Ce qui est vraiment nouveau ici, c'est de pouvoir Mongo 4.2référencer le document lui-même lors de sa mise à jour: la nouvelle valeur de "a"est basée sur la valeur existante de "$a".
    • Notez également "$toString"quel est un nouvel opérateur d'agrégation introduit dans Mongo 4.0.
  • N'oubliez pas { multi: true }, sinon seul le premier document correspondant sera mis à jour.


Dans le cas où votre casting est pas de deux à chaîne, vous avez le choix entre les différents opérateurs de conversion introduits dans Mongo 4.0tels que $toBool, $toInt...

Et s'il n'y a pas de convertisseur dédié pour votre type ciblé, vous pouvez le remplacer { $toString: "$a" }par une $convertopération: { $convert: { input: "$a", to: 2 } }où la valeur de tose trouve dans ce tableau :

db.collection.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $convert: { input: "$a", to: 2 } } } }],
  { multi: true }
)
Xavier Guihot
la source
1
db.collection.updateMany( { a : { $type: 1 } }, [{ $set: { a: { $toString: "$a" } } }] )- le multi : truepeut être évité en utilisantupdateMany
Vasim
1
À partir de 2020, l'utilisation de $ convert devrait être la bonne méthode pour cela, car elle devrait être beaucoup plus efficace (et plus facile à utiliser pour démarrer).
KhalilRavanna
10

toutes les réponses jusqu'à présent utilisent une version de forEach, itérant sur tous les éléments de la collection côté client.

Cependant, vous pouvez utiliser le traitement côté serveur de MongoDB en utilisant un pipeline agrégé et une étape $ out comme:

l'étape $ out remplace de manière atomique la collection existante par la nouvelle collection de résultats.

exemple:

db.documents.aggregate([
         {
            $project: {
               _id: 1,
               numberField: { $substr: ['$numberField', 0, -1] },
               otherField: 1,
               differentField: 1,
               anotherfield: 1,
               needolistAllFieldsHere: 1
            },
         },
         {
            $out: 'documents',
         },
      ]);
user3616725
la source
2
Je ne sais pas pourquoi ce n'est pas plus voté. Les opérations ligne par ligne sur de grands ensembles de données sont un meurtre sur les performances
Alf47
7

Pour convertir un champ de type chaîne en champ de date, vous devez itérer le curseur renvoyé par la find()méthode à l'aide de la forEach()méthode, dans la boucle, convertir le champ en objet Date, puis mettre à jour le champ à l'aide de l' $setopérateur.

Profitez de l'utilisation de l' API en masse pour les mises à jour en masse qui offrent de meilleures performances car vous enverrez les opérations au serveur par lots de, disons 1000, ce qui vous donne de meilleures performances car vous n'envoyez pas toutes les demandes au serveur, une seule fois dans chaque 1000 demandes.

Ce qui suit illustre cette approche, le premier exemple utilise l'API Bulk disponible dans les versions MongoDB >= 2.6 and < 3.2. Il met à jour tous les documents de la collection en modifiant tous lescreated_at champs en champs de date:

var bulk = db.collection.initializeUnorderedBulkOp(),
    counter = 0;

db.collection.find({"created_at": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
    var newDate = new Date(doc.created_at);
    bulk.find({ "_id": doc._id }).updateOne({ 
        "$set": { "created_at": newDate}
    });

    counter++;
    if (counter % 1000 == 0) {
        bulk.execute(); // Execute per 1000 operations and re-initialize every 1000 update statements
        bulk = db.collection.initializeUnorderedBulkOp();
    }
})
// Clean up remaining operations in queue
if (counter % 1000 != 0) { bulk.execute(); }

L'exemple suivant s'applique à la nouvelle version de MongoDB 3.2qui a depuis déconseillé l'API Bulk et fourni un ensemble plus récent d' API en utilisant bulkWrite():

var bulkOps = [];

db.collection.find({"created_at": {"$exists": true, "$type": 2 }}).forEach(function (doc) { 
    var newDate = new Date(doc.created_at);
    bulkOps.push(         
        { 
            "updateOne": { 
                "filter": { "_id": doc._id } ,              
                "update": { "$set": { "created_at": newDate } } 
            }         
        }           
    );     
})

db.collection.bulkWrite(bulkOps, { "ordered": true });
Chridam
la source
1
Excellente réponse, la méthode en bloc fonctionne environ 100 fois plus vite pour moi, même si cela semble toujours être un appel synchrone.
Matthieu a lu
3

Pour convertir int32 en chaîne dans mongo sans créer de tableau, ajoutez simplement "" à votre nombre :-)

db.foo.find( { 'mynum' : { $type : 16 } } ).forEach( function (x) {   
  x.mynum = x.mynum + ""; // convert int32 to string
  db.foo.save(x);
});
Giulio Roggero
la source
3

Ce qui m'a vraiment aidé à changer le type de l'objet dans MondoDB était juste cette simple ligne, peut-être mentionnée auparavant ici ...:

db.Users.find({age: {$exists: true}}).forEach(function(obj) {
    obj.age = new NumberInt(obj.age);
    db.Users.save(obj);
});

Les utilisateurs sont ma collection et l'âge est l'objet qui avait une chaîne au lieu d'un entier (int32).

Felipe
la source
1

J'ai besoin de changer le type de données de plusieurs champs de la collection, j'ai donc utilisé ce qui suit pour apporter plusieurs changements de type de données dans la collection de documents. Répondez à une vieille question mais peut être utile pour les autres.

db.mycoll.find().forEach(function(obj) { 

    if (obj.hasOwnProperty('phone')) {
        obj.phone = "" + obj.phone;  // int or longint to string
    }

    if (obj.hasOwnProperty('field-name')) {
     obj.field-name = new NumberInt(obj.field-name); //string to integer
    }

    if (obj.hasOwnProperty('cdate')) {
        obj.cdate = new ISODate(obj.cdate); //string to Date
    }

    db.mycoll.save(obj); 
});
Aakash
la source
1
You can easily convert the string data type to numerical data type.
Don't forget to change collectionName & FieldName.
for ex : CollectionNmae : Users & FieldName : Contactno.

Essayez cette requête.

db.collectionName.find().forEach( function (x) {
x.FieldName = parseInt(x.FieldName);
db.collectionName.save(x);
});
Amarendra Kumar
la source
1

demo change le type de champ mid de string à mongo objectId en utilisant mongoose

 Post.find({}, {mid: 1,_id:1}).exec(function (err, doc) {
             doc.map((item, key) => {
                Post.findByIdAndUpdate({_id:item._id},{$set:{mid: mongoose.Types.ObjectId(item.mid)}}).exec((err,res)=>{
                    if(err) throw err;
                    reply(res);
                });
            });
        });

Mongo ObjectId est juste un autre exemple de styles tels que

Nombre, chaîne, booléen qui espère que la réponse aidera quelqu'un d'autre.

Tran Hoang Hiep
la source
0

J'utilise ce script dans la console mongodb pour les conversions de chaîne à flotteur ...

db.documents.find({ 'fwtweaeeba' : {$exists : true}}).forEach( function(obj) { 
        obj.fwtweaeeba = parseFloat( obj.fwtweaeeba ); 
        db.documents.save(obj); } );    

db.documents.find({ 'versions.0.content.fwtweaeeba' : {$exists : true}}).forEach( function(obj) { 
        obj.versions[0].content.fwtweaeeba = parseFloat( obj.versions[0].content.fwtweaeeba ); 
        db.documents.save(obj); } );

db.documents.find({ 'versions.1.content.fwtweaeeba' : {$exists : true}}).forEach( function(obj) { 
        obj.versions[1].content.fwtweaeeba = parseFloat( obj.versions[1].content.fwtweaeeba );  
        db.documents.save(obj); } );

db.documents.find({ 'versions.2.content.fwtweaeeba' : {$exists : true}}).forEach( function(obj) { 
        obj.versions[2].content.fwtweaeeba = parseFloat( obj.versions[2].content.fwtweaeeba );  
        db.documents.save(obj); } );

Et celui-ci en php)))

foreach($db->documents->find(array("type" => "chair")) as $document){
    $db->documents->update(
        array('_id' => $document[_id]),
        array(
            '$set' => array(
                'versions.0.content.axdducvoxb' => (float)$document['versions'][0]['content']['axdducvoxb'],
                'versions.1.content.axdducvoxb' => (float)$document['versions'][1]['content']['axdducvoxb'],
                'versions.2.content.axdducvoxb' => (float)$document['versions'][2]['content']['axdducvoxb'],
                'axdducvoxb' => (float)$document['axdducvoxb']
            )
        ),
        array('$multi' => true)
    );


}
user3469031
la source
0

dans mon cas, j'utilise suivant

function updateToSting(){
  var collection = "<COLLECTION-NAME>";
  db.collection(collection).find().forEach(function(obj) {
    db.collection(collection).updateOne({YOUR_CONDITIONAL_FIELD:obj.YOUR_CONDITIONAL_FIELD},{$set:{YOUR_FIELD:""+obj.YOUR_FIELD}});
  });
}
Renish Gotecha
la source