Como alterar o tipo de um campo?


158

Estou tentando alterar o tipo de um campo de dentro do shell mongo.

Eu estou fazendo isso ...

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

Mas não está funcionando!


Se alguém estiver na mesma situação que eu, tendo toStringo campo de algum documento, aqui está o pequeno programa que eu fiz / usei .
Talles

Respostas:


213

A única maneira de alterar os $typedados é realizar uma atualização nos dados em que os dados têm o tipo correto.

Nesse caso, parece que você está tentando alterar o valor $type de 1 (duplo) para 2 (string) .

Portanto, basta carregar o documento no banco de dados, executar o cast ( new String(x)) e salvar o documento novamente.

Se você precisar fazer isso de forma programática e totalmente a partir do shell, poderá usar a find(...).forEach(function(x) {})sintaxe.


Em resposta ao segundo comentário abaixo. Altere o campo badde um número para uma sequência na coleção 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
Alguma chance de um exemplo - alterar um tipo de campo de int para string (ou vice-versa), a partir do shell?
Alister Bulman

30
no caso Int32-> String, new String(x.bad)cria uma coleção de Strings com o valor 0-index-item x.bad. Variant ""+x.bad, descrita por Simone trabalha como desejado - cria valor String em vez de Int32
Dao

O código acima está convertendo os dados do campo de duplo para array em vez de duplo para string. Meus dados real foi neste formato: 3.1 enquanto código simone está funcionando bem para mim
Pankaj Khurana

2
Tivemos uma situação onde eu precisava para converter o campo _id, bem como não conflito com outros í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 sim, você está reescrevendo um campo no banco de dados, isso é importante, não importa como você o faça. Se você estivesse usando SQL e essa fosse uma tabela grande, provavelmente teria que demorar um pouco.
Portões VP

161

Converter campo String em Inteiro:

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

Converta o campo Inteiro em String:

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

Isso é ótimo - você sabe como converter uma string (pense em uma moeda como '1,23') no número inteiro 123? Suponho que você precise analisá-lo como flutuante ou decimal, multiplique-o por 100 e salve-o como um número inteiro, mas não consigo encontrar os documentos certos para fazer isso. Obrigado!
Brian Armstrong

Na verdade, esse trabalho é bom. Mas eu tenho um aplicativo em execução com o mongoid 2.4.0-stable que possui campos como field: customer_count, tipo: Integer e uma validação como validates_numericality_of: customer_count que estava funcionando bem. Agora, quando estou atualizando para o mongoid para 3.0.16, quando atribuo um valor de string, ele automaticamente o converte em 0. sem erro. Eu quero lançar um erro na atribuição de dados incorreta, esse comportamento acaba sendo estranho para mim.
Swapnil Chincholkar

4
Eu executei isso e obtive o erro: Erro: não foi possível converter a seqüência de caracteres em número inteiro (shell): 1
Mittenchops

E se você precisar converter string (ou número inteiro "normal" de 32 bits) em número inteiro de 64 bits, use o seguinte NumberLong: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 bem. Posso saber como alterar tipo de dados dentro de campos de documentos incorporados
sankar muniyappa

43

Para string em conversão int.

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

Para string para dupla conversão.

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

Para flutuador:

    obj.my_value= parseFloat(obj.my_value);

2
Eu recomendaria também especificar o radix- developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
Russ Cam

5
Cuidado , eu testei isso com o Robomongo, e isso resultou no tipo 1: duplo. Teve que usar #new NumberInt()
911 Daniel F

Daniel, solução ur não é tudo bem ... A solução de david é ok
Rajib

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

15

Iniciando Mongo 4.2, db.collection.update()pode aceitar um pipeline de agregação, finalmente permitindo a atualização de um campo com base em seu próprio 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" }
  • A primeira parte { a : { $type: 1 } }é a consulta de correspondência:

    • Ele filtra quais documentos atualizar.
    • Nesse caso, como queremos converter "a"em string quando seu valor é duplo, isso corresponde a elementos para os quais "a"é do tipo 1(double)).
    • Esta tabela fornece os códigos que representam os diferentes tipos possíveis.
  • A segunda parte [{ $set: { a: { $toString: "$a" } } }]é o pipeline de agregação de atualização:

    • Observe os colchetes ao quadrado, significando que esta consulta de atualização usa um pipeline de agregação.
    • $seté um novo operador de agregação ( Mongo 4.2) que, nesse caso, modifica um campo.
    • Isso pode ser simplesmente lido como "$set"o valor de "a"a "$a"convertido "$toString".
    • O que é realmente novo aqui, é poder Mongo 4.2fazer referência ao próprio documento ao atualizá-lo: o novo valor para "a"é baseado no valor existente de "$a".
    • Observe também "$toString"qual é um novo operador de agregação introduzido no Mongo 4.0.
  • Não se esqueça { multi: true }, caso contrário, apenas o primeiro documento correspondente será atualizado.


No caso do seu elenco não é de dupla para string, você tem a escolha entre os operadores de conversão diferentes introduzidos em Mongo 4.0tais como $toBool, $toInt...

E se não houver um conversor dedicado para o seu tipo de destino, você poderá substituir { $toString: "$a" }por uma $convertoperação: { $convert: { input: "$a", to: 2 } }onde o valor de topode ser encontrado nesta tabela :

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" } } }] )- o multi : truepode ser evitado usandoupdateMany
Vasim

1
A partir de 2020, usar $ convert deve ser o método correto para isso, pois deve ser muito mais eficiente (e mais fácil de usar para inicializar).
KhalilRavanna 30/01

10

até agora todas as respostas usam alguma versão do forEach, repetindo todos os elementos da coleção no lado do cliente.

No entanto, você pode usar o processamento no servidor do MongoDB usando o pipeline agregado e o estágio $ out como:

o estágio $ out substitui atomicamente a coleção existente pela nova coleção de resultados.

exemplo:

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

2
Não sei por que isso não é mais votado. As operações linha a linha em grandes conjuntos de dados são assassinatos por desempenho
Alf47

7

Para converter um campo do tipo de seqüência de caracteres em campo de data, você precisará iterar o cursor retornado pelo find()método usando o forEach()método, dentro do loop, converter o campo em um objeto Date e, em seguida, atualizar o campo usando o método$set operador.

Aproveite o uso do API em massa para atualizações em massa que oferecem melhor desempenho, pois você envia as operações para o servidor em lotes de, digamos, 1000, o que oferece um desempenho melhor, pois você não envia todas as solicitações ao servidor, apenas uma vez a cada 1000 pedidos.

A seguir demonstra essa abordagem, o primeiro exemplo usa a API em massa disponível nas versões do MongoDB >= 2.6 and < 3.2. Ele atualiza todos os documentos da coleção, alterando todos os created_atcampos para os campos de 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(); }

O próximo exemplo se aplica à nova versão do MongoDB, 3.2que descontinuou a API em massa e forneceu um conjunto mais novo de APIs usando 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
Ótima resposta, o método em massa é 100x mais rápido para mim, mesmo que pareça uma chamada síncrona.
Matthew Leia

3

Para converter int32 em string no mongo sem criar uma matriz, adicione "" ao seu número :-)

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

3

O que realmente me ajudou a alterar o tipo de objeto no MondoDB foi apenas esta linha simples, talvez mencionada antes aqui ...:

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

Usuários são minha coleção e age é o objeto que tinha uma string em vez de um número inteiro (int32).


1

Eu preciso alterar o tipo de dados de vários campos na coleção, portanto, usei o seguinte para fazer várias alterações no tipo de dados na coleção de documentos. Responda a uma pergunta antiga, mas pode ser útil para outras pessoas.

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.

Tente esta consulta ..

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

1

demo altera o tipo de campo no meio da string para o mongo objectId usando o 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);
                });
            });
        });

O Mongo ObjectId é apenas mais um exemplo de estilos como

Número, string, booleano que esperam que a resposta ajude outra pessoa.


0

Eu uso esse script no console do mongodb para que a string flutue conversões ...

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 este em 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

no meu caso, eu uso o seguinte

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}});
  });
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.