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


159

Я пытаюсь изменить тип поля в оболочке Монго.

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

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

Но это не работает!


Если кто-то находится в той же ситуации, что и я, имея toStringполе для какого-либо документа, вот небольшая программа, которую я сделал / использовал .
talles

Ответы:


214

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

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

Поэтому просто загрузите документ из БД, выполните приведение ( 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);
});

1
Есть ли шанс примера - изменить тип поля с int на строку (или наоборот) из оболочки?
Алистер Булман

30
в случае Int32-> String new String(x.bad)создает коллекцию Strings со значением 0-index-item x.bad. Вариант ""+x.bad, описанный Симоной, работает как нужно - создает значение String вместо Int32
Dao

Приведенный выше код преобразует данные поля из двойного в массив вместо двойного в строку. Мои фактические данные были в этом формате: 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); });
Мэтт Молнар

@SundarBons да, вы переписываете поле в своей базе данных, это большое дело, независимо от того, как вы это делаете. Если бы вы использовали SQL, и это была большая таблица, вам, вероятно, потребовалось бы некоторое время простоя.
Гейтс 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, а затем сохранить как целое число, но я не могу найти нужные документы для этого. Спасибо!
Брайан Армстронг

На самом деле это работает хорошо. Но у меня есть приложение, работающее с mongoid 2.4.0-stable, у которого есть такие поля, как field: customer_count, type: Integer и валидация as validates_numericity_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

это работает нормально.
Могу ли

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: double. Пришлось использоватьnew NumberInt()
Даниэль Ф

Даниэль, твое решение не в порядке ... решение
Раджиб

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

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"в строку, когда ее значение равно double, это соответствует элементам, для которых "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
Васим

1
Начиная с 2020 года, использование $ convert должно быть правильным методом для этого, так как он должен быть намного более эффективным (и более простым в использовании для загрузки).
Халил Раванна

10

все ответы до сих пор используют некоторую версию 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',
         },
      ]);

2
Я не знаю, почему за это больше не голосуют.
Строковые

7

Чтобы преобразовать поле строкового типа в поле даты, вам нужно будет перебрать курсор, возвращаемый 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 и предоставила более новый набор 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 раз быстрее, хотя кажется, что это синхронный вызов.
Мэтью Читал

3

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

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

Пользователи - это моя коллекция, а age - это объект, в котором вместо целого числа была строка (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

Демо изменить тип поля середины от строки до монго 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}});
  });
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.