Làm thế nào để thay đổi loại của một lĩnh vực?


158

Tôi đang cố gắng thay đổi loại trường từ trong vỏ mongo.

Tôi đang làm việc này...

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

Nhưng nó không hoạt động!


Nếu bất cứ ai ở trong hoàn cảnh tương tự như tôi, phải có toStringmột số lĩnh vực tài liệu, đây là chương trình nhỏ tôi đã thực hiện / sử dụng .
cao

Câu trả lời:


213

Cách duy nhất để thay đổi $typedữ liệu là thực hiện cập nhật dữ liệu trong đó dữ liệu có đúng loại.

Trong trường hợp này, có vẻ như bạn đang cố gắng thay đổi $type từ 1 (gấp đôi) thành 2 (chuỗi) .

Vì vậy, chỉ cần tải tài liệu từ DB, thực hiện cast ( new String(x)) và sau đó lưu tài liệu lại.

Nếu bạn cần thực hiện việc này theo chương trình và hoàn toàn từ shell, bạn có thể sử dụng find(...).forEach(function(x) {})cú pháp.


Đáp lại bình luận thứ hai dưới đây. Thay đổi trường badtừ một số thành một chuỗi trong bộ sưu tập 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
Bất kỳ cơ hội nào của một ví dụ - thay đổi một loại trường từ int thành chuỗi (hoặc ngược lại), từ shell?
Alister Bulman

30
trong trường hợp Int32-> String, new String(x.bad)tạo bộ sưu tập Chuỗi có x.badgiá trị 0-index-item . Biến thể ""+x.bad, được mô tả bởi Simone hoạt động như mong muốn - tạo giá trị Chuỗi thay vì Int32
Dao

Đoạn mã trên đang chuyển đổi dữ liệu trường từ double sang mảng thay vì double thành chuỗi. Dữ liệu thực tế của tôi ở định dạng này: 3.1 trong khi mã simone hoạt động tốt với tôi
Pankaj Khurana

2
Có một tình huống mà tôi cần để chuyển đổi trường _id cũng như không xung đột với các chỉ mục khác: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 vâng, bạn đang viết lại một trường trên cơ sở dữ liệu của bạn, đây là một vấn đề lớn cho dù bạn làm thế nào. Nếu bạn đang sử dụng SQL và đây là một bảng lớn, có lẽ bạn sẽ phải mất một chút thời gian.
Gates VP

161

Chuyển đổi trường Chuỗi thành Số nguyên:

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

Chuyển đổi trường Integer thành String:

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

Điều này thật tuyệt - bạn có biết cách bạn chuyển đổi một chuỗi (nghĩ loại tiền như '1,23') sang số nguyên 123 không? Tôi giả sử bạn phải phân tích cú pháp dưới dạng dấu phẩy hoặc số thập phân, nhân nó với 100, sau đó lưu nó dưới dạng số nguyên, nhưng tôi không thể tìm thấy các tài liệu phù hợp để làm điều này. Cảm ơn!
Brian Armstrong

Trên thực tế công việc này là tốt. Nhưng tôi có một ứng dụng đang chạy với mongoid 2.4.0 ổn định, có các trường như trường: customer_count, loại: Integer & xác thực là validates_numericality_of: customer_count đang hoạt động tốt. Bây giờ khi tôi nâng cấp lên mongoid lên 3.0.16, khi tôi gán giá trị chuỗi, nó sẽ tự động chuyển đổi thành 0. mà không gặp lỗi. Tôi muốn ném một lỗi vào việc gán dữ liệu sai, hành vi này trở nên kỳ lạ đối với tôi.
Swapnil Chincholkar

4
Tôi đã chạy cái này và gặp lỗi: Lỗi: không thể chuyển đổi chuỗi thành số nguyên (shell): 1
Găng tay vào

Và nếu bạn cần chuyển đổi chuỗi (hoặc số nguyên 32 bit "bình thường") thành số nguyên 64 bit, hãy sử dụng NumberLongnhư sau: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

nó hoạt động tốt Tôi có thể biết cách thay đổi kiểu dữ liệu trong các trường tài liệu được nhúng không
sankar muniyappa 28/03/2016

43

Đối với chuỗi chuyển đổi int.

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

Đối với chuỗi chuyển đổi gấp đôi.

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

Đối với phao:

    obj.my_value= parseFloat(obj.my_value);

2
Tôi cũng khuyên bạn nên chỉ định radix- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ chủ
Russ Cam

5
Xem ra , tôi đã thử nghiệm điều này với Robomongo và kết quả là loại 1: gấp đôi. Phải sử dụngnew NumberInt()
Daniel F

Daniel, giải pháp của bạn không ổn ... giải pháp của david là ok
Rajib

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

15

Bắt đầu Mongo 4.2,db.collection.update() có thể chấp nhận một đường ống tổng hợp, cuối cùng cho phép cập nhật trường dựa trên giá trị của chính nó:

// { 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" }
  • Phần đầu tiên { a : { $type: 1 } }là truy vấn khớp:

    • Nó lọc những tài liệu để cập nhật.
    • Trong trường hợp này, vì chúng tôi muốn chuyển đổi "a"thành chuỗi khi giá trị của nó là gấp đôi, nên điều này khớp với các phần tử có "a"kiểu1 (double)).
    • Bảng này cung cấp các mã đại diện cho các loại khác nhau có thể.
  • Phần thứ hai [{ $set: { a: { $toString: "$a" } } }]là đường ống tổng hợp cập nhật:

    • Lưu ý dấu ngoặc vuông biểu thị rằng truy vấn cập nhật này sử dụng đường ống tổng hợp.
    • $set là một toán tử tổng hợp mới (Mongo 4.2 ) trong trường hợp này sửa đổi một trường.
    • Điều này có thể chỉ đơn giản là đọc như "$set"giá trị của "a"để "$a"chuyển đổi "$toString".
    • Điều thực sự mới ở đây, là có thể Mongo 4.2tham chiếu chính tài liệu khi cập nhật nó: giá trị mới "a"dựa trên giá trị hiện có của"$a" .
    • Cũng lưu ý "$toString"đó là một toán tử tổng hợp mới được giới thiệu trong Mongo 4.0.
  • Đừng quên { multi: true }, nếu không thì chỉ có tài liệu phù hợp đầu tiên sẽ được cập nhật.


Trong trường hợp diễn viên của bạn không từ gấp đôi thành chuỗi, bạn có quyền lựa chọn giữa các toán tử chuyển đổi khác nhau được giới thiệu Mongo 4.0như $toBool,$toInt , ...

Và nếu không có bộ chuyển đổi chuyên dụng cho loại mục tiêu của bạn, bạn có thể thay thế { $toString: "$a" }bằng một $convertthao tác: { $convert: { input: "$a", to: 2 } }nơi tocó thể tìm thấy giá trị trong bảng này :

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 : truecó thể tránh sử dụngupdateMany
Vasim

1
Kể từ năm 2020, sử dụng $ convert nên là phương pháp chính xác cho việc này vì nó sẽ hiệu quả hơn rất nhiều (và dễ sử dụng hơn để khởi động).
KhalilRavanna

10

tất cả các câu trả lời cho đến nay đều sử dụng một số phiên bản forEach, lặp đi lặp lại trên tất cả các phần tử bộ sưu tập phía máy khách.

Tuy nhiên, bạn có thể sử dụng xử lý phía máy chủ của MongoDB bằng cách sử dụng đường ống tổng hợp và giai đoạn $ out như:

giai đoạn $ out về cơ bản thay thế bộ sưu tập hiện có bằng bộ sưu tập kết quả mới.

thí dụ:

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

2
Tôi không biết tại sao điều này không được nâng cao hơn nữa. Hoạt động theo từng hàng trên các tập dữ liệu lớn là giết người về hiệu suất
Alf47

7

Để chuyển đổi trường kiểu chuỗi thành trường ngày, bạn sẽ cần lặp lại con trỏ được trả về bởi find()phương thức bằng forEach()phương thức, trong vòng lặp chuyển đổi trường thành đối tượng Ngày và sau đó cập nhật trường bằng cách sử dụng$set toán tử.

Tận dụng lợi thế của việc sử dụng API hàng loạt cho các bản cập nhật hàng loạt mang lại hiệu suất tốt hơn vì bạn sẽ gửi các hoạt động đến máy chủ theo đợt 1000, mang lại cho bạn hiệu suất tốt hơn khi bạn không gửi mọi yêu cầu đến máy chủ, chỉ một lần trong mỗi 1000 yêu cầu.

Sau đây minh họa cách tiếp cận này, ví dụ đầu tiên sử dụng API số lượng lớn có sẵn trong các phiên bản MongoDB >= 2.6 and < 3.2. Nó cập nhật tất cả các tài liệu trong bộ sưu tập bằng cách thay đổi tất cả các created_attrường thành các trường ngày:

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

Ví dụ tiếp theo áp dụng cho phiên bản MongoDB mới 3.2, từ đó đã loại bỏ API hàng loạt và cung cấp một bộ apis mới hơn bằng cách sử dụng 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
Câu trả lời tuyệt vời, phương thức số lượng lớn chạy nhanh hơn khoảng 100 lần đối với tôi mặc dù nó vẫn có vẻ là một cuộc gọi đồng bộ.
Matthew Đọc

3

Để chuyển đổi int32 thành chuỗi trong mongo mà không cần tạo một mảng, chỉ cần thêm "" vào số của bạn :-)

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

3

Điều thực sự giúp tôi thay đổi loại đối tượng trong MondoDB chỉ là dòng đơn giản này, có lẽ được đề cập trước đây ...:

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

Người dùng là bộ sưu tập của tôi và tuổi là đối tượng có chuỗi thay vì số nguyên (int32).


1

Tôi cần thay đổi kiểu dữ liệu của nhiều trường trong bộ sưu tập, vì vậy tôi đã sử dụng cách sau để thực hiện nhiều thay đổi loại dữ liệu trong bộ sưu tập tài liệu. Trả lời một câu hỏi cũ nhưng có thể hữu ích cho những người khác.

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.

Hãy thử truy vấn này ..

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

1

bản demo thay đổi loại trường giữa từ chuỗi sang mongo objectId bằng cách sử dụng 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 chỉ là một ví dụ khác về các phong cách như

Số, chuỗi, boolean hy vọng câu trả lời sẽ giúp người khác.


0

Tôi sử dụng tập lệnh này trong bảng điều khiển mongodb cho chuỗi để chuyển đổi ...

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

Và cái này bằng 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

trong trường hợp của tôi, tôi sử dụng sau

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}});
  });
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.