¿Cómo cambiar el tipo de campo?

159

Estoy tratando de cambiar el tipo de campo desde dentro del shell mongo.

Yo estoy haciendo esto...

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

¡Pero no está funcionando!

J. Quintas
fuente
Si alguien está en la misma situación que yo, teniendo que ver toStringel campo de algún documento, aquí está el pequeño programa que hice / usé .
Talles

Respuestas:

214

La única forma de cambiar los $typedatos es realizar una actualización de los datos donde los datos tienen el tipo correcto.

En este caso, parece que estás tratando de cambiar $type de 1 (doble) a 2 (cadena) .

Así que simplemente cargue el documento desde la base de datos, realice la conversión ( new String(x)) y luego guarde el documento nuevamente.

Si necesita hacer esto programáticamente y completamente desde el shell, puede usar la find(...).forEach(function(x) {})sintaxis.


En respuesta al segundo comentario a continuación. Cambie el campo badde un número a una cadena en la colección foo.

db.foo.find( { 'bad' : { $type : 1 } } ).forEach( function (x) {   
  x.bad = new String(x.bad); // convert field to string
  db.foo.save(x);
});
Vicepresidente de Gates
fuente
1
¿Alguna posibilidad de un ejemplo: cambiar un tipo de campo de int a string (o viceversa), desde el shell?
Alister Bulman
30
en caso de Int32-> String, new String(x.bad)crea una colección de Strings con x.badvalor de elemento de índice 0 . La variante ""+x.bad, descrita por Simone, funciona según lo deseado: crea un valor de cadena en lugar de Int32
Dao
El código anterior está convirtiendo los datos de campo de doble a matriz en lugar de doble a cadena. Mis datos reales estaban en este formato: 3.1 mientras que el código simone funciona bien para mí
Pankaj Khurana
2
Tuve una situación en la que necesitaba convertir el campo _id, así como no entrar en conflicto con otros índices: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 sí, está reescribiendo un campo en su base de datos, esto es un gran problema, no importa cómo lo haga. Si estaba utilizando SQL y esta era una tabla grande, probablemente tendría que tomarse un tiempo de inactividad.
Gates VP
161

Convertir campo de cadena a entero:

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 campo entero a cadena:

db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) {
    obj.field-name = "" + obj.field-name;
    db.db-name.save(obj);
});
Simone
fuente
Esto es genial: ¿sabe cómo convertiría una cadena (piense en una moneda como '1.23') al número entero 123? Supongo que tendría que analizarlo como flotante o decimal, multiplíquelo por 100, luego guárdelo como un entero, pero no puedo encontrar los documentos correctos para hacerlo. ¡Gracias!
Brian Armstrong
En realidad este trabajo es bueno. Pero tengo una aplicación que se ejecuta con mongoid 2.4.0-stable que tiene campos como field: customer_count, type: Integer y una validación como validates_numericality_of: customer_count que funcionaba bien. Ahora, cuando estoy actualizando a mongoid a 3.0.16, cuando asigno un valor de cadena, lo convierte automáticamente a 0. sin error. Quiero arrojar un error en la asignación de datos incorrecta, este comportamiento me resulta extraño.
Swapnil Chincholkar 01 de
44
Ejecuté esto y obtuve el error: Error: no se pudo convertir la cadena a entero (shell): 1
Mittenchops
Y si necesita convertir una cadena (o un entero "normal" de 32 bits) a un entero de 64 bits, utilícelo NumberLongcomo aquí: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
Funciona bien. ¿
Puedo
43

Para la conversión de cadena a int.

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

Para cadena a doble conversión.

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

Para flotador:

    obj.my_value= parseFloat(obj.my_value);
David Dehghan
fuente
2
Recomendaría también especificar el radix- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Russ Cam
55
Cuidado , probé esto con Robomongo, y esto resultó en el tipo 1: doble. Tuve que usarnew NumberInt()
Daniel F
Daniel, tu solución no está bien ... la solución de
David
22
db.coll.find().forEach(function(data) {
    db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})
Russell
fuente
15

Al comenzar Mongo 4.2, db.collection.update()puede aceptar una canalización de agregación, finalmente permitiendo la actualización de un campo en función de su propio valor:

// { 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 primera parte { a : { $type: 1 } }es la consulta de coincidencia:

    • Filtra qué documentos actualizar.
    • En este caso, dado que queremos convertir "a"a cadena cuando su valor es doble, esto coincide con elementos para los que "a"es de tipo 1(doble)).
    • Esta tabla proporciona los códigos que representan los diferentes tipos posibles.
  • La segunda parte [{ $set: { a: { $toString: "$a" } } }]es la tubería de agregación de actualizaciones:

    • Tenga en cuenta los corchetes que significan que esta consulta de actualización utiliza una canalización de agregación.
    • $setes un nuevo operador de agregación ( Mongo 4.2) que en este caso modifica un campo.
    • Esto puede ser simplemente leído como "$set"el valor de "a"a "$a"convertido "$toString".
    • Lo que es realmente nuevo aquí es poder Mongo 4.2hacer referencia al documento en sí mismo al actualizarlo: el nuevo valor para "a"se basa en el valor existente de "$a".
    • También tenga en cuenta "$toString"cuál es un nuevo operador de agregación introducido en Mongo 4.0.
  • No lo olvide { multi: true }, de lo contrario solo se actualizará el primer documento coincidente.


En caso de que el yeso no es de doble cadena a, usted tiene la posibilidad de elegir entre diferentes operadores de conversión introducidos en Mongo 4.0tales como $toBool, $toInt...

Y si no hay un convertidor dedicado para su tipo de destino, puede reemplazarlo { $toString: "$a" }con una $convertoperación: { $convert: { input: "$a", to: 2 } }donde el valor de tose puede encontrar en esta tabla :

db.collection.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $convert: { input: "$a", to: 2 } } } }],
  { multi: true }
)
Xavier Guihot
fuente
1
db.collection.updateMany( { a : { $type: 1 } }, [{ $set: { a: { $toString: "$a" } } }] )- multi : trueSe puede evitar usandoupdateMany
Vasim
1
A partir de 2020, usar $ convert debería ser el método correcto para esto, ya que debería ser mucho más eficiente (y más fácil de usar para arrancar).
KhalilRavanna
10

todas las respuestas hasta ahora usan alguna versión de forEach, iterando sobre todos los elementos de la colección del lado del cliente.

Sin embargo, puede utilizar el procesamiento del lado del servidor de MongoDB utilizando la canalización agregada y la etapa $ out como:

la etapa $ out reemplaza atómicamente la colección existente con la nueva colección de resultados.

ejemplo:

db.documents.aggregate([
         {
            $project: {
               _id: 1,
               numberField: { $substr: ['$numberField', 0, -1] },
               otherField: 1,
               differentField: 1,
               anotherfield: 1,
               needolistAllFieldsHere: 1
            },
         },
         {
            $out: 'documents',
         },
      ]);
usuario3616725
fuente
2
No sé por qué esto no se vota más. Fila por fila operaciones en grandes conjuntos de datos son el asesinato en el rendimiento
Alf47
7

Para convertir un campo de tipo de cadena en un campo de fecha, deberá iterar el cursor devuelto por el find()método que usa el forEach()método, dentro del bucle, convierta el campo en un objeto Fecha y luego actualice el campo usando el $setoperador.

Aproveche el uso de la API masiva para actualizaciones masivas que ofrecen un mejor rendimiento, ya que enviará las operaciones al servidor en lotes de, digamos 1000, lo que le brinda un mejor rendimiento ya que no envía todas las solicitudes al servidor, solo una vez cada 1000 solicitudes.

Lo siguiente demuestra este enfoque, el primer ejemplo usa la API Bulk disponible en versiones MongoDB >= 2.6 and < 3.2. Actualiza todos los documentos de la colección cambiando todos los created_atcampos a campos de fecha:

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

El siguiente ejemplo se aplica a la nueva versión de MongoDB 3.2que desde entonces ha desaprobado la API masiva y ha proporcionado un nuevo conjunto de API utilizando 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
fuente
1
Gran respuesta, el método masivo se ejecuta aproximadamente 100 veces más rápido para mí, aunque todavía parece ser una llamada sincrónica.
Mateo leyó el
3

Para convertir int32 a una cadena en mongo sin crear una matriz simplemente agregue "" a su número :-)

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

Lo que realmente me ayudó a cambiar el tipo de objeto en MondoDB fue solo esta línea simple, tal vez mencionada antes aquí:

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

Los usuarios son mi colección y age es el objeto que tenía una cadena en lugar de un entero (int32).

Felipe
fuente
1

Necesito cambiar el tipo de datos de múltiples campos en la colección, por lo que utilicé lo siguiente para hacer cambios de tipo de datos múltiples en la colección de documentos. Responda a una pregunta anterior, pero puede ser útil para otros.

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

Prueba esta consulta ...

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

tipo de cambio de demostración del campo medio de string a mongo objectId usando 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 es solo otro ejemplo de estilos como

Número, cadena, booleano que esperan que la respuesta ayude a alguien más.

Tran Hoang Hiep
fuente
0

Utilizo este script en la consola mongodb para que las cadenas floten en conversiones ...

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

Y este 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)
    );


}
usuario3469031
fuente
0

en mi caso, uso siguiente

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}});
  });
}
Renote Gotecha
fuente