Come cambiare il tipo di un campo?


159

Sto cercando di cambiare il tipo di campo dall'interno della shell mongo.

Lo sto facendo ...

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

Ma non funziona!


Se qualcuno si trova nella stessa situazione in cui mi trovavo, dovendo andare toStringnel campo di alcuni documenti, ecco il piccolo programma che ho creato / usato .
Talles,

Risposte:


214

L'unico modo per modificare i $typedati è eseguire un aggiornamento sui dati in cui i dati hanno il tipo corretto.

In questo caso, sembra che tu stia provando a cambiare $type da 1 (doppio) a 2 (stringa) .

Quindi è sufficiente caricare il documento dal DB, eseguire il cast ( new String(x)) e quindi salvare nuovamente il documento.

Se è necessario farlo in modo programmatico e interamente dalla shell, è possibile utilizzare la find(...).forEach(function(x) {})sintassi.


In risposta al secondo commento qui sotto. Cambia il campo badda un numero a una stringa nella raccolta foo.

db.foo.find( { 'bad' : { $type : 1 } } ).forEach( function (x) {   
  x.bad = new String(x.bad); // convert field to string
  db.foo.save(x);
});

1
Qualche possibilità di esempio - cambiare un tipo di campo da int a string (o viceversa), dalla shell?
Alister Bulman,

30
nel caso Int32-> String, new String(x.bad)crea una raccolta di stringhe con x.badvalore di elemento di indice 0 . La variante ""+x.baddescritta da Simone funziona come desiderato: crea il valore String anziché Int32
Dao

Il codice sopra sta convertendo i dati del campo da double a array anziché da double a string. I miei dati reali erano in questo formato: 3.1 mentre il codice simone funziona bene per me
Pankaj Khurana

2
Ho avuto una situazione in cui dovevo convertire il campo _id e non entrare in conflitto con altri indici: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ì, stai riscrivendo un campo nel tuo database, questo è un grosso problema, non importa come lo fai. Se stavi usando SQL e questa era una grande tabella, probabilmente dovresti prenderti un po 'di tempo.
Gates VP,

161

Converti campo String in Intero:

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

Converti campo intero in stringa:

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

Questo è fantastico: sai come convertiresti una stringa (pensi a una valuta come "1,23") nell'intero 123? Suppongo che dovresti analizzarlo come float o decimale, moltiplicarlo per 100, quindi salvarlo come numero intero, ma non riesco a trovare i documenti giusti per farlo. Grazie!
Brian Armstrong,

In realtà questo lavoro è buono. Ma ho un'applicazione in esecuzione con mongoid 2.4.0-stable che ha campi come field: customer_count, tipo: Integer e una convalida come validates_numericality_of: customer_count che funzionava bene. Ora quando eseguo l'aggiornamento a Mongoide a 3.0.16, quando assegno un valore di stringa, lo converte automaticamente in 0. senza errori. Voglio gettare un errore nell'assegnazione errata dei dati, questo comportamento risulta strano per me.
Swapnil Chincholkar,

4
Ho eseguito questo e ho ricevuto l'errore: Errore: impossibile convertire la stringa in numero intero (shell): 1
Mittenchops

E se hai bisogno di convertire stringa (o "normale" intero a 32 bit) a 64 bit integer, l'uso NumberLongcome qui: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

funziona benissimo. Posso sapere come modificare il tipo di dati all'interno dei campi del documento incorporato
sankar muniyappa,

43

Per la conversione da stringa a int.

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

Per la conversione da stringa a doppia.

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

Per galleggiante:

    obj.my_value= parseFloat(obj.my_value);

2
Consiglierei anche di specificare radix- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Russ Cam

5
Attenzione , ho provato questo con Robomongo e questo ha portato al tipo 1: doppio. Ho dovuto usarenew NumberInt()
Daniel F

Daniel, la tua soluzione non va bene ... la soluzione di David è ok
Rajib

22
db.coll.find().forEach(function(data) {
    db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})

15

A partire da Mongo 4.2, db.collection.update()può accettare una pipeline di aggregazione, consentendo infine l'aggiornamento di un campo basato sul proprio valore:

// { 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 prima parte { a : { $type: 1 } }è la query di corrispondenza:

    • Filtra i documenti da aggiornare.
    • In questo caso, poiché vogliamo convertire "a"in stringa quando il suo valore è doppio, questo corrisponde a elementi per i quali "a"è di tipo 1(doppio)).
    • Questa tabella fornisce i codici che rappresentano i diversi tipi possibili.
  • La seconda parte [{ $set: { a: { $toString: "$a" } } }]è la pipeline di aggregazione degli aggiornamenti:

    • Notare le parentesi quadre che indicano che questa query di aggiornamento utilizza una pipeline di aggregazione.
    • $setè un nuovo operatore di aggregazione ( Mongo 4.2) che in questo caso modifica un campo.
    • Questo può essere semplicemente letto come "$set"il valore di "a"per "$a"convertire "$toString".
    • Ciò che è veramente nuovo qui, è la possibilità di Mongo 4.2fare riferimento al documento stesso durante l'aggiornamento: il nuovo valore per "a"si basa sul valore esistente di "$a".
    • Si noti inoltre "$toString"che è stato introdotto un nuovo operatore di aggregazione Mongo 4.0.
  • Non dimenticare { multi: true }, altrimenti verrà aggiornato solo il primo documento corrispondente.


Nel caso in cui il cast non è da doppia a stringa, si ha la possibilità di scegliere tra diversi operatori di conversione introdotte nel Mongo 4.0quale $toBool, $toInt...

E se non esiste un convertitore dedicato per il tipo di destinazione, è possibile sostituirlo { $toString: "$a" }con $convertun'operazione: { $convert: { input: "$a", to: 2 } }dove il valore per topuò essere trovato in questa tabella :

db.collection.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $convert: { input: "$a", to: 2 } } } }],
  { multi: true }
)

1
db.collection.updateMany( { a : { $type: 1 } }, [{ $set: { a: { $toString: "$a" } } }] )- si multi : truepuò evitare di usareupdateMany
Vasim

1
A partire dal 2020, l'utilizzo di $ convert dovrebbe essere il metodo corretto per questo in quanto dovrebbe essere molto più efficiente (e più facile da usare per l'avvio).
KhalilRavanna, il

10

tutte le risposte finora utilizzano una versione di forEach, ripetendo sul lato client tutti gli elementi di raccolta.

Tuttavia, è possibile utilizzare l'elaborazione lato server di MongoDB utilizzando la pipeline aggregata e $ out stage come:

lo stage $ out sostituisce atomicamente la raccolta esistente con la nuova raccolta di risultati.

esempio:

db.documents.aggregate([
         {
            $project: {
               _id: 1,
               numberField: { $substr: ['$numberField', 0, -1] },
               otherField: 1,
               differentField: 1,
               anotherfield: 1,
               needolistAllFieldsHere: 1
            },
         },
         {
            $out: 'documents',
         },
      ]);

2
Non so perché questo non sia più votato. Operazioni su file su file di grandi dimensioni sono omicidi durante le esibizioni
Alf47,

7

Per convertire un campo di tipo stringa in un campo data, è necessario ripetere il cursore restituito dal find()metodo utilizzando il forEach()metodo, all'interno del ciclo convertire il campo in un oggetto Date e quindi aggiornare il campo utilizzando l' $setoperatore.

Approfitta dell'utilizzo dell'API di massa per gli aggiornamenti di massa che offrono prestazioni migliori in quanto invierai le operazioni al server in batch di diciamo 1000 che ti offrono prestazioni migliori in quanto non invii tutte le richieste al server, una volta ogni 1000 richieste.

Di seguito viene illustrato questo approccio, il primo esempio utilizza l'API in blocco disponibile nelle versioni MongoDB >= 2.6 and < 3.2. Aggiorna tutti i documenti della raccolta modificando tutti i created_atcampi in campi data:

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

Il prossimo esempio si applica alla nuova versione di MongoDB 3.2che da allora ha deprecato l'API in blocco e fornito una nuova serie di API utilizzando 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 });

1
Ottima risposta, il metodo bulk esegue circa 100 volte più veloce per me, anche se sembra essere ancora una chiamata sincrona.
Matteo Leggi l'

3

Per convertire int32 in stringa in mongo senza creare un array basta aggiungere "" al tuo numero :-)

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

3

Ciò che mi ha davvero aiutato a cambiare il tipo di oggetto in MondoDB è stata proprio questa semplice linea, forse menzionata prima qui ...:

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

Gli utenti sono la mia raccolta e age è l'oggetto che aveva una stringa anziché un numero intero (int32).


1

Devo modificare il tipo di dati di più campi nella raccolta, quindi ho usato quanto segue per apportare più modifiche al tipo di dati nella raccolta di documenti. Rispondi a una vecchia domanda ma può essere utile per gli altri.

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

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.

Prova questa query ..

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

1

demo cambia tipo di campo al centro da stringa a oggetto mongoId 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 è solo un altro esempio di stili come

Numero, stringa, booleano che sperano che la risposta aiuti qualcun altro.


0

Uso questo script nella console di mongodb per stringare conversioni fluttuanti ...

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

E questo 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)
    );


}

0

nel mio caso, io uso il seguente

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}});
  });
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.