Wie ändere ich den Typ eines Feldes?

158

Ich versuche, den Typ eines Feldes innerhalb der Mongo-Shell zu ändern.

Ich mache das ...

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

Aber es funktioniert nicht!

J. Quintas
quelle
Wenn sich jemand in der gleichen Situation befindet wie ich und toStringdas Feld eines Dokuments benötigt, ist hier das kleine Programm, das ich erstellt / verwendet habe .
Talles

Antworten:

213

Die einzige Möglichkeit, $typedie Daten zu ändern, besteht darin, eine Aktualisierung der Daten durchzuführen, bei denen die Daten den richtigen Typ haben.

In diesem Fall scheinen Sie zu versuchen, die Zahl $type von 1 (doppelt) in 2 (Zeichenfolge) zu ändern .

Laden Sie also einfach das Dokument aus der Datenbank, führen Sie cast ( new String(x)) aus und speichern Sie das Dokument erneut.

Wenn Sie dies programmgesteuert und vollständig über die Shell ausführen müssen, können Sie die find(...).forEach(function(x) {})Syntax verwenden.


Als Antwort auf den zweiten Kommentar unten. Ändern Sie das Feld badvon einer Zahl in eine Zeichenfolge in der Sammlung foo.

db.foo.find( { 'bad' : { $type : 1 } } ).forEach( function (x) {   
  x.bad = new String(x.bad); // convert field to string
  db.foo.save(x);
});
Gates VP
quelle
1
Gibt es eine Chance für ein Beispiel - das Ändern eines Feldtyps von int in string (oder umgekehrt) aus der Shell?
Alister Bulman
30
Im Fall Int32-> String wird new String(x.bad)eine Sammlung von Strings mit dem x.badWert 0-index-item erstellt . Die ""+x.badvon Simone beschriebene Variante funktioniert wie gewünscht - erstellt einen String-Wert anstelle von Int32
Dao
Der obige Code konvertiert die Felddaten von double in array anstelle von double in string. Meine tatsächlichen Daten waren in diesem Format: 3.1, während Simone-Code für mich gut funktioniert
Pankaj Khurana
2
Hatte eine Situation, in der ich das _id-Feld konvertieren musste und nicht mit anderen Indizes in Konflikt stand: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 Ja, Sie schreiben ein Feld in Ihrer Datenbank neu. Dies ist eine große Sache, egal wie Sie es tun. Wenn Sie SQL verwenden und dies eine große Tabelle ist, müssten Sie wahrscheinlich einige Ausfallzeiten in Anspruch nehmen.
Gates VP
161

String-Feld in Integer konvertieren:

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

Ganzzahlfeld in Zeichenfolge konvertieren:

db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) {
    obj.field-name = "" + obj.field-name;
    db.db-name.save(obj);
});
Simone
quelle
Das ist großartig - wissen Sie, wie Sie eine Zeichenfolge (denken Sie an eine Währung wie '1.23') in die Ganzzahl 123 konvertieren würden? Ich gehe davon aus, dass Sie es als Gleitkomma- oder Dezimalzahl analysieren, mit 100 multiplizieren und dann als Ganzzahl speichern müssen, aber ich kann nicht die richtigen Dokumente finden, um dies zu tun. Vielen Dank!
Brian Armstrong
Eigentlich ist das gut. Aber ich habe eine Anwendung, die mit Mongoid 2.4.0-Stable ausgeführt wird und Felder wie Feld: customer_count, Typ: Integer und eine Validierung als validates_numericality_of: customer_count enthält, die einwandfrei funktioniert hat. Wenn ich jetzt ein Upgrade auf Mongoid auf 3.0.16 durchführe und einen Zeichenfolgenwert zuweise, wird dieser automatisch ohne Fehler in 0 konvertiert. Ich möchte einen Fehler bei falscher Datenzuweisung auslösen. Dieses Verhalten ist für mich seltsam.
Swapnil Chincholkar
4
Ich habe dies ausgeführt und den Fehler erhalten: Fehler: String konnte nicht in Integer (Shell) konvertiert werden: 1
Mittenchops
Und wenn Sie Zeichenfolge (oder "normale" 32-Bit-Ganzzahl) in 64-Bit-Ganzzahl konvertieren müssen, verwenden Sie NumberLongwie db.db-name.find({field-name : {$exists : true}}).forEach( function(obj) { obj.field-name = new NumberLong(obj.field-name); db.db-name.save(obj); } );
folgt
es funktioniert gut. Darf ich wissen, wie man den Datentyp in eingebetteten Dokumentfeldern
ändert
43

Für die Konvertierung von Zeichenfolgen in int.

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

Für die Konvertierung von Zeichenfolgen in doppelte.

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

Für Schwimmer:

    obj.my_value= parseFloat(obj.my_value);
David Dehghan
quelle
2
Ich würde empfehlen, auch die radix- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Russ Cam
5
Achtung , ich habe dies mit Robomongo getestet und dies führte zu Typ 1: double. Musste verwendennew NumberInt()
Daniel F
Daniel, deine Lösung ist nicht in Ordnung ... Davids Lösung ist in Ordnung
Rajib
22
db.coll.find().forEach(function(data) {
    db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})
Russell
quelle
15

Starten Mongo 4.2, db.collection.update()kann eine Aggregation Pipeline akzeptieren, schließlich die Aktualisierung eines Feldes ermöglicht , basierend auf seinen eigenen Wert:

// { 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" }
  • Der erste Teil { a : { $type: 1 } }ist die Übereinstimmungsabfrage:

    • Es filtert, welche Dokumente aktualisiert werden sollen.
    • In diesem Fall entspricht "a"dies Elementen, für die "a"der Typ 1(double) ist , da wir in einen String konvertieren möchten, wenn sein Wert ein Double ist .
    • Diese Tabelle enthält die Codes, die die verschiedenen möglichen Typen darstellen.
  • Der zweite Teil [{ $set: { a: { $toString: "$a" } } }]ist die Update-Aggregations-Pipeline:

    • Beachten Sie die eckigen Klammern, die angeben, dass diese Aktualisierungsabfrage eine Aggregationspipeline verwendet.
    • $setist ein neuer Aggregationsoperator ( Mongo 4.2), der in diesem Fall ein Feld ändert.
    • Dies kann einfach gelesen werden als "$set"der Wert der "a"zu "$a"umgewandelt "$toString".
    • Was hier wirklich neu ist, ist die Möglichkeit Mongo 4.2, beim Aktualisieren auf das Dokument selbst zu verweisen: Der neue Wert für "a"basiert auf dem vorhandenen Wert von "$a".
    • Beachten Sie auch, "$toString"welcher neue Aggregationsoperator in eingeführt wurde Mongo 4.0.
  • Vergessen Sie nicht { multi: true }, sonst wird nur das erste übereinstimmende Dokument aktualisiert.


Falls Ihr Guss nicht doppelt zu bespannen, haben Sie die Wahl zwischen verschiedenen Konvertierungsoperator in eingeführt Mongo 4.0, wie $toBool, $toInt...

Und wenn es für Ihren Zieltyp keinen dedizierten Konverter gibt, können Sie ihn durch { $toString: "$a" }eine $convertOperation ersetzen : { $convert: { input: "$a", to: 2 } }Der Wert für tobefindet sich in dieser Tabelle :

db.collection.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $convert: { input: "$a", to: 2 } } } }],
  { multi: true }
)
Xavier Guihot
quelle
1
db.collection.updateMany( { a : { $type: 1 } }, [{ $set: { a: { $toString: "$a" } } }] )- Das multi : truekann vermieden werden mitupdateMany
Vasim
1
Ab 2020 sollte die Verwendung von $ convert die richtige Methode sein, da sie viel effizienter (und einfacher zu booten) sein sollte.
KhalilRavanna
10

Alle bisherigen Antworten verwenden eine Version von forEach, die clientseitig über alle Sammlungselemente iteriert.

Sie können jedoch die serverseitige Verarbeitung von MongoDB verwenden, indem Sie die aggregierte Pipeline und die $ out-Phase wie folgt verwenden :

Die Phase $ out ersetzt atomar die vorhandene Sammlung durch die neue Ergebnissammlung.

Beispiel:

db.documents.aggregate([
         {
            $project: {
               _id: 1,
               numberField: { $substr: ['$numberField', 0, -1] },
               otherField: 1,
               differentField: 1,
               anotherfield: 1,
               needolistAllFieldsHere: 1
            },
         },
         {
            $out: 'documents',
         },
      ]);
user3616725
quelle
2
Ich weiß nicht, warum dies nicht mehr positiv bewertet wird. Zeile für Zeile Operationen an großen Datenmengen sind Mord an der Leistung
Alf47
7

Um ein Feld vom Typ Zeichenfolge in ein Datumsfeld zu konvertieren, müssen Sie den von der find()Methode zurückgegebenen Cursor mithilfe von iterierenforEach() Methode zurückgegebenen , das Feld innerhalb der Schleife in ein Datumsobjekt konvertieren und das Feld dann mit dem $setOperator aktualisieren .

Nutzen Sie die Bulk-API für Bulk-Updates, die eine bessere Leistung bieten, da Sie die Vorgänge in Stapeln von beispielsweise 1000 an den Server senden. Dies führt zu einer besseren Leistung, da Sie nicht jede Anforderung nur einmal an den Server senden 1000 Anfragen.

Im Folgenden wird dieser Ansatz veranschaulicht. Im ersten Beispiel wird die in MongoDB-Versionen verfügbare Bulk-API verwendet >= 2.6 and < 3.2 . . Es aktualisiert alle Dokumente in der Sammlung, indem alle created_atFelder in Datumsfelder geändert werden:

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(); }

Das nächste Beispiel gilt für die neue MongoDB-Version, 3.2die seitdem die Bulk-API veraltet und einen neueren Satz von APIs bereitgestellt hat, indem sie 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
quelle
1
Tolle Antwort, die Bulk-Methode läuft für mich ungefähr 100x schneller, obwohl es immer noch ein synchroner Aufruf zu sein scheint.
Matthew Read
3

Um int32 in mongo in einen String umzuwandeln, ohne ein Array zu erstellen, fügen Sie einfach "" zu Ihrer Nummer hinzu :-)

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

Was mir wirklich geholfen hat, den Typ des Objekts in MondoDB zu ändern, war nur diese einfache Zeile, die hier vielleicht schon erwähnt wurde ...:

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

Benutzer sind meine Sammlung und Alter ist das Objekt, das eine Zeichenfolge anstelle einer Ganzzahl (int32) hatte.

Felipe
quelle
1

Ich muss den Datentyp mehrerer Felder in der Sammlung ändern, daher habe ich Folgendes verwendet, um mehrere Datentypänderungen in der Sammlung von Dokumenten vorzunehmen. Beantworten Sie eine alte Frage, aber sie kann für andere hilfreich sein.

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
quelle
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.

Versuchen Sie diese Abfrage ..

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

Demo Ändern Sie den Feldtyp in der Mitte von String zu Mongo ObjectId mit Mungo

 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 ist nur ein weiteres Beispiel für Stile wie

Zahl, Zeichenfolge, Boolescher Wert, die hoffen, dass die Antwort jemand anderem hilft.

Tran Hoang Hiep
quelle
0

Ich benutze dieses Skript in der Mongodb-Konsole für String-Float-Konvertierungen ...

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); } );

Und dieser in 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
quelle
0

In meinem Fall verwende ich Folgendes

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
quelle