Bir alanın tipi nasıl değiştirilir?


159

Mongo kabuğunun içinden bir alanın türünü değiştirmeye çalışıyorum.

Bunu yapıyorum ...

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

Ama işe yaramıyor!


Eğer herhangi bir kişi benimle aynı durumda ise, toStringbir belge alanına sahip olmak , yaptığım / kullandığım küçük program .
boylu

Yanıtlar:


214

Verileri değiştirmenin tek yolu $type, verilerin doğru türde olduğu veriler üzerinde bir güncelleme yapmaktır.

Bu durumda, $type 1'i (çift) 2'ye (dize) değiştirmeye çalıştığınız anlaşılıyor .

Bu nedenle belgeyi DB'den yükleyin, cast ( new String(x)) işlemini gerçekleştirin ve ardından belgeyi tekrar kaydedin.

Bunu programlı ve tamamen kabuktan yapmanız gerekiyorsa, find(...).forEach(function(x) {})sözdizimini kullanabilirsiniz .


Aşağıdaki ikinci yoruma yanıt olarak. Alanı badbir sayıdan koleksiyondaki bir dizeye değiştirin 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
Herhangi bir örnek şansı - kabuktan int alanından dizeye (ya da tam tersi) bir alan türünü değiştirmek?
Alister Bulman

30
Int32-> String durumunda, new String(x.bad)0-index-item x.baddeğerine sahip Dizeler koleksiyonu oluşturur . ""+x.badSimone tarafından açıklanan varyant istendiği gibi çalışır - Int32 yerine String değeri oluşturur
Dao

Yukarıdaki kod, alan verilerini çift yerine dizeye dönüştürüyor. Gerçek verilerim şu formattaydı: 3.1 iken simone kodu benim için iyi çalışıyor
Pankaj Khurana

2
_İd alanını dönüştürmek ve diğer dizinlerle çakışmamak için gerekli bir durum vardı: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 evet veritabanınız boyunca bir alanı yeniden yazıyorsunuz, bunu nasıl yaparsanız yapın çok önemli. SQL kullanıyorsanız ve bu büyük bir tablo olsaydı, muhtemelen biraz zaman ayırmak gerekir.
Gates VP

161

Dize alanını Tamsayıya Dönüştür:

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

Tamsayı alanını Dizeye Dönüştür:

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

Bu harika - bir dizeyi ('1.23' gibi para birimini) tamsayı 123'e nasıl dönüştüreceğinizi biliyor musunuz? Bir kayan nokta veya ondalık sayı olarak ayrıştırmanız, 100 ile çarpmanız ve ardından bir tamsayı olarak kaydetmeniz gerektiğini varsayıyorum, ancak bunu yapmak için doğru belgeleri bulamıyorum. Teşekkürler!
Brian Armstrong

Aslında bu çalışma iyi. Ama alan: customer_count, türü: Tamsayı ve validates_numericality_of: iyi çalışıyordu müşteri_count gibi bir doğrulama gibi mongoid 2.4.0 kararlı çalışan bir uygulama var. Şimdi mongoid 3.0.16'ya yükseltme yaparken, bir dize değeri atadığımda otomatik olarak 0'a bir hata olmadan dönüştürür. Yanlış veri atamasına hata atmak istiyorum, bu davranış benim için garip çıkıyor.
Swapnil Chincholkar

4
Bunu çalıştırdım ve hatayı aldım: Hata: string tamsayıya (shell) dönüştürülemedi: 1
Mittenchops

Ve dize (veya "normal" 32 bit tam sayı) 64 bit tam sayıya dönüştürmeniz gerekiyorsa, şu şekilde kullanın 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

iyi çalışıyor. Gömülü belge alanlarındaki veri türünü nasıl değiştirebilirim?
Sankar Muniyappa

43

Dizeden int'ye dönüşüm için.

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

Dize - çift dönüşüm için.

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

Şamandıra için:

    obj.my_value= parseFloat(obj.my_value);


5
Dikkat et , bunu Robomongo ile test ettim ve bu da tip 1: double ile sonuçlandı. Kullanmak zorundanew NumberInt()
Daniel F

Daniel, çözümünüz iyi değil ... david'in çözümü iyi
Rajib

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

15

Başlangıçta Mongo 4.2, db.collection.update()bir toplama pipeline'ını kabul edebilir ve sonunda bir alanın kendi değerine göre güncellenmesine izin verebilir:

// { 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" }
  • İlk bölüm { a : { $type: 1 } }eşleme sorgusudur:

    • Hangi belgelerin güncelleneceğini filtreler.
    • Bu durumda, "a"değeri bir çift olduğunda dizeye dönüştürmek istediğimizden , bu "a", türü 1(double) türündeki öğelerle eşleşir .
    • Bu tablo , olası farklı türleri temsil eden kodları sağlar.
  • İkinci bölüm [{ $set: { a: { $toString: "$a" } } }], güncelleme toplama boru hattıdır:

    • Bu güncelleştirme sorgusunun bir toplama işlemi kullandığını belirten köşeli parantezlere dikkat edin.
    • $setMongo 4.2bu durumda bir alanı değiştiren yeni bir toplama operatörüdür ( ).
    • Bu basitçe okunabilir "$set"değerine "a"kadar "$a"dönüştürülür "$toString".
    • Burada gerçekten yeni olan şey Mongo 4.2, belgeyi güncellerken kendisine referans verebilmektir : için yeni değer "a", mevcut değerine dayanmaktadır "$a".
    • Ayrıca "$toString", hangi yeni bir toplama operatörü olduğunu tanıttı Mongo 4.0.
  • Unutmayın { multi: true }, aksi takdirde yalnızca eşleşen ilk belge güncellenir.


Durumda Kadronuzun değil çift gelen dizeye, sen tanıtılan farklı dönüşüm operatörleri arasında seçim var Mongo 4.0gibi $toBool, $toInt...

Hedeflediğiniz tür için özel bir dönüştürücü yoksa, { $toString: "$a" }bir $convertişlemle değiştirebilirsiniz : { $convert: { input: "$a", to: 2 } }için değeri tobu tabloda bulunabilir :

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" } } }] )- multi : truekullanarak kaçınılabilirupdateMany
Vasim

1
2020'den itibaren, $ convert kullanmak bunun için doğru yöntem olmalıdır, çünkü çok daha verimli (ve önyükleme için kullanımı daha kolay) olmalıdır.
KhalilRavanna

10

Şimdiye kadar tüm cevaplar, istemci tarafı tüm koleksiyon öğeleri üzerinde yineleme, forEach bazı sürümünü kullanır.

Ancak, toplam boru hattı ve $ out aşamasını kullanarak MongoDB'nin sunucu tarafı işlemesini şu şekilde kullanabilirsiniz :

$ out aşaması, atomik olarak mevcut koleksiyonu yeni sonuç koleksiyonuyla değiştirir.

misal:

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

2
Bunun neden daha fazla değerlendirilmediğini bilmiyorum. Büyük veri kümelerinde satır satır işlemler performansta cinayet
işlemektedir

7

Dize türü bir alanı tarih alanına dönüştürmek için, find()yöntemi kullanarak yöntem tarafından döndürülen imleci yinelemeniz gerekir forEach(), döngü içinde alanı bir Date nesnesine dönüştürür ve ardından $setoperatörü kullanarak alanı güncelleştirmeniz gerekir .

Daha iyi performans sunan toplu güncellemeler için Toplu API'yı kullanmanın avantajlarından yararlanın, çünkü işlemleri sunucuya 1000 gibi toplu olarak gönderirsiniz, bu da her isteği sunucuya göndermediğiniz için size daha iyi bir performans sunar 1000 istek.

Aşağıda bu yaklaşım gösterilmektedir, ilk örnek MongoDB sürümlerinde bulunan Toplu API'yı kullanmaktadır >= 2.6 and < 3.2. Tüm created_atalanları tarih alanlarına değiştirerek koleksiyondaki tüm belgeleri günceller :

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

Bir sonraki örnek , Toplu API'yı kullanımdan kaldırmış ve aşağıdakileri kullanarak daha yeni bir apis seti 3.2sağlayan yeni MongoDB sürümü için geçerlidir :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
Büyük cevap, toplu yöntem hala senkronize bir çağrı gibi görünse de benim için yaklaşık 100x daha hızlı çalışır.
Matthew

3

Bir dizi oluşturmadan int32'yi mongo'da dizeye dönüştürmek için numaranıza "" ekleyin :-)

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

3

MondoDB'deki nesnenin türünü değiştirmeme gerçekten yardımcı olan şey, belki de burada daha önce de belirtildiği gibi basit bir çizgiydi ...:

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

Kullanıcılar benim koleksiyonumdur ve yaş, tamsayı (int32) yerine dize içeren nesnedir.


1

Ben koleksiyonda birden çok alanın veri türünü değiştirmek gerekiyor, bu yüzden aşağıdaki belge koleksiyonunda birden çok veri türü değişiklikleri yapmak için kullanılır. Eski bir soruya cevap verin ama başkaları için yararlı olabilir.

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.

Bu sorguyu deneyin ..

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

1

demo dizgiden mongo nesnesine orta alan türünü değiştir

 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, bu tarzlara sadece bir başka örnektir

Sayı, dize, boole cevabının başka birine yardımcı olacağını umuyoruz.


0

Ben dönüşümleri kayan dize için mongodb konsolunda bu komut dosyası kullanın ...

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

Ve php bu))))

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

benim durumumda aşağıdakileri kullanıyorum

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}});
  });
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.