MongoDB: Как изменить тип поля?

В Stackru уже есть вопрос, очень похожий на мой вопрос. Дело в том, что ответ на эти вопросы был для драйвера Java, я пытаюсь сделать это в оболочке.

Я делаю это...

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

Это не работает!

15 ответов

Решение

Единственный способ изменить $type данных для выполнения обновления данных, где данные имеют правильный тип.

В этом случае похоже, что вы пытаетесь изменить $type от 1 (двойной) до 2 (строка).

Так что просто загрузите документ из БД, выполните приведение (new String(x)), а затем сохраните документ снова.

Если вам нужно сделать это программно и полностью из оболочки, вы можете использовать find(...).forEach(function(x) {}) синтаксис.


В ответ на второй комментарий ниже. Изменить поле bad из числа в строку в коллекции foo,

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

Преобразовать строковое поле в целое число:

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

Запуск 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 (двойной)).
    • В этой таблице представлены коды, представляющие различные возможные типы.
  • Вторая часть [{ $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 }
)

Для преобразования строки в 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);
db.coll.find().forEach(function(data) {
    db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})

Все ответы до сих пор используют некоторую версию forEach, перебирая все элементы коллекции на стороне клиента.

Однако вы можете использовать обработку на стороне сервера MongoDB, используя агрегатный конвейер и $out stage как:

стадия $ out атомарно заменяет существующую коллекцию новой коллекцией результатов.

пример:

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

Чтобы преобразовать поле строкового типа в поле даты, вам нужно будет перебрать курсор, возвращаемый find() метод с использованием forEach() метод, внутри цикла преобразовать поле в объект Date, а затем обновить поле с помощью $set оператор.

Воспользуйтесь преимуществом Bulk API для массовых обновлений, которые обеспечивают более высокую производительность, поскольку вы будете отправлять операции на сервер партиями, скажем, 1000, что даст вам лучшую производительность, поскольку вы не отправляете каждый запрос на сервер, только один раз в каждом 1000 запросов.

Следующее демонстрирует этот подход, первый пример использует Bulk API, доступный в версиях MongoDB >= 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 который с тех пор устарел Bulk API и предоставил более новый набор API, используя 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 });

Чтобы преобразовать int32 в строку в монго, не создавая массив, просто добавьте "" к своему номеру:-)

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

Что действительно помогло мне изменить тип объекта в MondoDB, так это простая строка, возможно, упоминавшаяся ранее здесь:

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

Пользователи - это моя коллекция, а age - это объект, в котором вместо целого числа была строка (int32).

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

Демо изменить тип поля середины от строки до монго 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 - это еще один пример таких стилей, как

Число, строка, логическое значение, которые надеются, что ответ поможет кому-то еще.

Приведенные выше ответы почти сработали, но имели несколько проблем:

Проблема 1:db.collection.saveбольше не работает в MongoDB 5.x

Для этого я использовалreplaceOne().

Проблема 2:new String(x.bad)давал экспоненциальное число

я использовал"" + x.badкак было предложено выше.

Моя версия:

      let count = 0;

db.user
    .find({
        custID: {$type: 1},
    })
    .forEach(function (record) {
        count++;

        const actualValue = record.custID;
        record.custID = "" + record.custID;
        console.log(`${count}. Updating User(id:${record._id}) from old id [${actualValue}](${typeof actualValue}) to [${record.custID}](${typeof record.custID})`)

        db.user.replaceOne({_id: record._id}, record);
    });

И для миллионов записей вот результат (для дальнейшего исследования/справки)-

Мне нужно изменить тип данных нескольких полей в коллекции, поэтому я использовал следующее для внесения нескольких изменений типа данных в коллекцию документов. Ответ на старый вопрос, но может быть полезным для других.

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

Я использую этот скрипт в консоли 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)
    );


}

В моем случае я использую следующие

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}});
  });
}
Другие вопросы по тегам