จะเปลี่ยนประเภทของเขตข้อมูลได้อย่างไร?


158

ฉันพยายามที่จะเปลี่ยนประเภทของเขตข้อมูลจากภายในเปลือก Mongo

ฉันกำลังทำสิ่งนี้ ...

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

แต่มันไม่ทำงาน!


ถ้าใครอยู่ในสถานการณ์เดียวกันผมก็มีการtoStringข้อมูลของเอกสารบางอย่างที่นี่เป็นโปรแกรมเล็ก ๆ ที่ฉันได้ทำ
สูง

คำตอบ:


213

วิธีเดียวที่จะเปลี่ยน $typeข้อมูลคือทำการอัปเดตข้อมูลที่มีข้อมูลประเภทที่ถูกต้อง

ในกรณีนี้ดูเหมือนว่าคุณกำลังพยายามที่จะเปลี่ยน$type จาก 1 (คู่) 2 (สตริง)

ดังนั้นเพียงแค่โหลดเอกสารจากฐานข้อมูลทำการ cast ( new String(x)) จากนั้นบันทึกเอกสารอีกครั้ง

หากคุณต้องการทำสิ่งนี้โดยทางโปรแกรมและทั้งหมดจากเชลล์คุณสามารถใช้find(...).forEach(function(x) {})ไวยากรณ์ได้


เพื่อตอบสนองต่อความคิดเห็นที่สองด้านล่าง เปลี่ยนสนามจากหมายเลขที่จะสตริงในคอลเลกชันbadfoo

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

1
โอกาสใด ๆ ของตัวอย่าง - เปลี่ยนชนิดของฟิลด์จาก int เป็นสตริง (หรือในทางกลับกัน), จากเชลล์?
Alister Bulman

30
ในกรณี Int32-> สตริงnew String(x.bad)สร้างคอลเลกชันของสายด้วย 0 ดัชนีรายการx.badค่า Variant ที่""+x.badSimone อธิบายไว้ทำงานได้ตามต้องการสร้างค่า String แทน Int32
Dao

โค้ดด้านบนกำลังแปลงข้อมูลฟิลด์จาก double เป็น array แทนที่จะเป็น double เป็น string ข้อมูลที่แท้จริงของฉันเป็นในรูปแบบนี้: 3.1 ขณะที่รหัส simone คือการทำงานที่ดีสำหรับฉัน
Pankaj Khurana

2
มีสถานการณ์ที่ฉันต้องการแปลงฟิลด์ _id รวมถึงไม่ขัดแย้งกับดัชนีอื่น ๆ :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 ใช่คุณกำลังเขียนฟิลด์ในฐานข้อมูลของคุณใหม่นี่เป็นเรื่องใหญ่ไม่ว่าคุณจะทำอย่างไร ถ้าคุณใช้ SQL และนี่เป็นตารางใหญ่คุณอาจต้องใช้เวลาสักพัก
Gates VP

161

แปลงฟิลด์สตริงเป็นจำนวนเต็ม:

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

แปลงฟิลด์จำนวนเต็มเป็นสตริง:

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

เป็นสิ่งที่ดี - คุณรู้ไหมว่าคุณแปลงสตริงอย่างไร (คิดว่าสกุลเงินเช่น '1.23') เป็นจำนวนเต็ม 123 ฉันคิดว่าคุณต้องแยกมันออกเป็นทศนิยมหรือทศนิยมคูณด้วย 100 แล้วบันทึกเป็นจำนวนเต็ม แต่ฉันไม่พบเอกสารที่ถูกต้องในการทำเช่นนี้ ขอบคุณ!
Brian Armstrong

อันที่จริงการทำงานนี้เป็นสิ่งที่ดี แต่ฉันมีแอปพลิเคชันที่ทำงานด้วย mongoid 2.4.0-เสถียรซึ่งมีฟิลด์เช่นฟิลด์: customer_count, ประเภท: จำนวนเต็ม & การตรวจสอบความถูกต้องเป็น validates_numericality_of: customer_count ซึ่งทำงานได้ดี ตอนนี้เมื่อฉันอัพเกรดเป็น mongoid เป็น 3.0.16 เมื่อฉันกำหนดค่าสตริงมันจะแปลงเป็น 0 โดยอัตโนมัติโดยไม่มีข้อผิดพลาด ฉันต้องการที่จะโยนข้อผิดพลาดในการกำหนดข้อมูลที่ไม่ถูกต้องพฤติกรรมนี้กลายเป็นเรื่องแปลกสำหรับฉัน
Swapnil Chincholkar

4
ฉันวิ่งและได้รับข้อผิดพลาด: ข้อผิดพลาด: ไม่สามารถแปลงสตริงเป็นจำนวนเต็ม (เชลล์): 1
Mittenchops

และหากคุณต้องการแปลงสตริง (หรือ "ปกติ" จำนวนเต็ม 32 บิต) เป็นจำนวนเต็ม 64 บิตให้ใช้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

มันใช้งานได้ดี ฉันขอทราบวิธีการเปลี่ยนประเภทข้อมูลภายในช่องเอกสารที่ฝังอยู่ได้อย่างไร
sankar muniyappa

43

สำหรับการแปลงสตริงเป็น int

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

สำหรับสตริงการแปลงสองครั้ง

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

สำหรับลอย:

    obj.my_value= parseFloat(obj.my_value);

2
ฉันอยากจะแนะนำให้ระบุradix- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Russ Cam

5
ระวังนะฉันทดสอบมันด้วย Robomongo และนี่ก็ส่งผลให้ประเภท 1: สองเท่า ต้องใช้new NumberInt()
Daniel F

Daniel ทางออกของคุณไม่เป็นไร ... ทางออกของ david ไม่เป็นไร
Rajib


15

เริ่มต้นMongo 4.2, db.collection.update()สามารถยอมรับท่อรวมในที่สุดก็ช่วยให้การปรับปรุงข้อมูลขึ้นอยู่กับค่าของตัวเอง:

// { 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 : { $type: 1 } }คือข้อความค้นหาที่ตรงกัน:

    • มันกรองเอกสารที่จะอัปเดต
    • ในกรณีนี้เนื่องจากเราต้องการแปลง"a"เป็นสตริงเมื่อค่าของมันเป็นสองเท่านี่จึงจับคู่องค์ประกอบที่"a"เป็นประเภท1 (double)
    • ตารางนี้มีรหัสที่แสดงถึงประเภทที่แตกต่างกัน
  • ส่วนที่สอง[{ $set: { a: { $toString: "$a" } } }]คือขั้นตอนการรวมการอัพเดท:

    • โปรดสังเกตว่าวงเล็บเหลี่ยมกำลังสองที่บ่งบอกว่าแบบสอบถามแบบใช้ปรับปรุงนี้ใช้ไปป์ไลน์การรวม
    • $set เป็นผู้ประกอบการรวมใหม่ (Mongo 4.2 ) ซึ่งในกรณีนี้จะปรับเปลี่ยนเขตข้อมูล
    • สามารถอ่าน"$set"ค่านี้เป็นค่าของ"a"การ"$a"แปลง"$toString"แปลง
    • มีอะไรใหม่ที่นี่จริง ๆ สามารถMongo 4.2อ้างอิงเอกสารเองได้เมื่อทำการอัพเดท: ค่าใหม่สำหรับ"a"จะขึ้นอยู่กับมูลค่าที่มีอยู่ของ"$a"จะขึ้นอยู่กับค่าที่มีอยู่ของ
    • นอกจากนี้โปรดทราบ"$toString"ว่าตัวดำเนินการรวมตัวใหม่ที่นำมาใช้Mongo 4.0คืออะไร
  • อย่าลืม{ multi: true }มิฉะนั้นเฉพาะเอกสารการจับคู่แรกเท่านั้นที่จะได้รับการอัปเดต


ในกรณีที่นักแสดงของคุณไม่ได้มาจากคู่สตริงคุณมีทางเลือกระหว่างผู้ประกอบการแปลงที่แตกต่างกันนำมาใช้ในMongo 4.0เช่น$toBool, $toInt...

และหากไม่มีตัวแปลงเฉพาะสำหรับประเภทเป้าหมายของคุณคุณสามารถแทนที่{ $toString: "$a" }ด้วยการ$convertดำเนินการ: { $convert: { input: "$a", to: 2 } }ซึ่งค่าtoสามารถพบได้ในตารางนี้:

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 : trueสามารถหลีกเลี่ยงได้โดยใช้updateMany
Vasim

1
ตั้งแต่ปี 2020 การใช้การแปลง $ ควรเป็นวิธีที่ถูกต้องสำหรับสิ่งนี้เนื่องจากมันควรมีประสิทธิภาพมากกว่า (และใช้งานง่ายกว่าในการบูต)
KhalilRavanna

10

คำตอบทั้งหมดจนถึงตอนนี้ใช้ forEach บางเวอร์ชันซ้ำกับองค์ประกอบคอลเลกชันทั้งหมดที่ฝั่งไคลเอ็นต์

อย่างไรก็ตามคุณสามารถใช้การประมวลผลฝั่งเซิร์ฟเวอร์ของ MongoDB โดยใช้ขั้นตอนการสรุปรวมและขั้นตอนการ $ outเป็น:

$ out stage จะแทนที่คอลเล็กชันที่มีอยู่ด้วยการรวบรวมผลลัพธ์ใหม่

ตัวอย่าง:

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

2
ฉันไม่รู้ว่าทำไมสิ่งนี้ถึงไม่ได้รับการโหวตเพิ่มอีก การดำเนินการแบบแถวต่อแถวบนชุดข้อมูลขนาดใหญ่เป็นการฆาตกรรมที่มีประสิทธิภาพ
Alf47

7

ในการแปลงเขตข้อมูลของชนิดสตริงเป็นเขตข้อมูลวันที่คุณจะต้องวนซ้ำเคอร์เซอร์ที่ส่งคืนโดยfind()วิธีการใช้forEach()วิธีการภายในวงวนแปลงเขตข้อมูลเป็นวัตถุวันที่แล้วอัปเดตเขตข้อมูลโดยใช้ตัว$setดำเนินการ

ใช้ประโยชน์จากการใช้APIจำนวนมากสำหรับการอัปเดตจำนวนมากซึ่งให้ประสิทธิภาพที่ดีกว่าเนื่องจากคุณจะส่งการดำเนินการไปยังเซิร์ฟเวอร์ด้วยจำนวน 1,000 รายการซึ่งให้ประสิทธิภาพที่ดีกว่าเนื่องจากคุณไม่ได้ส่งคำขอไปยังเซิร์ฟเวอร์เพียงครั้งเดียว 1,000 คำขอ

ต่อไปนี้จะแสดงให้เห็นถึงวิธีการนี้ตัวอย่างแรกใช้ API >= 2.6 and < 3.2เป็นกลุ่มที่มีอยู่ในรุ่น มันอัพเดตเอกสารทั้งหมดในคอลเลกชันโดยการเปลี่ยนcreated_atฟิลด์ทั้งหมดเป็นฟิลด์วันที่:

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

ตัวอย่างถัดไปนำไปใช้กับ MongoDB เวอร์ชันใหม่3.2ซึ่งได้คัดค้าน API จำนวนมากและให้ชุด apis ใหม่ที่ใช้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
คำตอบที่ดีวิธีการแบบกลุ่มจะทำงานได้เร็วขึ้นประมาณ 100 เท่าสำหรับฉันแม้ว่าจะยังดูเหมือนเป็นการโทรแบบซิงโครนัส
Matthew อ่าน

3

ในการแปลง int32 เป็นสตริงใน mongo โดยไม่ต้องสร้างอาร์เรย์เพียงเพิ่ม "" ไปยังหมายเลขของคุณ :-)

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

3

สิ่งที่ช่วยให้ฉันเปลี่ยนประเภทของวัตถุใน MondoDB เป็นเพียงบรรทัดง่าย ๆ ที่กล่าวถึงก่อนหน้านี้ ... :

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

ผู้ใช้คือคอลเล็กชันของฉันและอายุเป็นวัตถุที่มีสตริงแทนจำนวนเต็ม (int32)


1

ฉันต้องการเปลี่ยนประเภทข้อมูลของเขตข้อมูลหลายแห่งในคอลเลกชันดังนั้นฉันจึงใช้สิ่งต่อไปนี้เพื่อทำการเปลี่ยนแปลงประเภทข้อมูลหลายชุดในการรวบรวมเอกสาร ตอบคำถามเก่า แต่อาจมีประโยชน์สำหรับผู้อื่น

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.

ลองใช้แบบสอบถามนี้ ..

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

1

ประเภทการเปลี่ยนแปลงการสาธิตของฟิลด์กลางจากสตริงไปยัง Mongo objectId โดยใช้พังพอน

 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 เป็นเพียงตัวอย่างของสไตล์เช่น

ตัวเลข, สตริง, บูลีนที่หวังว่าคำตอบจะช่วยคนอื่น


0

ฉันใช้สคริปต์นี้ในคอนโซล mongodb สำหรับสตริงที่จะแปลงลอย ...

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

และอันนี้ใน 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

ในกรณีของฉันฉันใช้ต่อไปนี้

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}});
  });
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.