Як змінити тип поля?


158

Я намагаюся змінити тип поля з оболонки монго.

Я роблю це ...

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) {})синтаксис.


У відповідь на другий коментар нижче. Змініть поле 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)створює колекцію рядків зі значенням 0-index-item x.bad. Варіант ""+x.bad, описаний Simone працює за бажанням - створює значення String замість Int32
Дао

Вищевказаний код - це перетворення даних поля з подвійного в масив замість подвійного в рядковий. Мої фактичні дані були у такому форматі: 3.1, тоді як код Simone працює для мене чудово
Панкай Хурана

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 і це була велика таблиця, вам, ймовірно, доведеться зайняти трохи часу.
Гейтс ВП

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

Перетворити поле Integer в String:

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, а потім зберегти його як ціле число, але я не можу знайти потрібних документів для цього. Дякую!
Брайан Армстронг

Насправді це працює добре. Але у мене є програма, що працює з монгоїдним 2.4.0-стабільним, який містить такі поля, як поле: customer_count, type: Integer & validation as validates_numericality_of: customer_count, який працював нормально. Тепер, коли я переходжу на монгоїд до 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()
Даніель Ф

Даніель, ур-рішення не в порядку ... Рішення
Дейвіда

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"в рядок, коли його значення є подвійним, це відповідає елементам, для яких "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 як:

етап $ 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оператора.

Скористайтеся перевагою використання групового API для масових оновлень, які пропонують кращу продуктивність, оскільки ви будете відправляти операції на сервер партіями, наприклад, 1000, що дає вам кращу ефективність, оскільки ви не надсилаєте кожен запит на сервер, а раз у раз 1000 запитів.

Далі демонструється такий підхід, перший приклад використовує груповий 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яка з тих пір припинила груповий 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);
});

Користувачі - це моя колекція, а вік - це об'єкт, який мав рядок замість цілого числа (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}});
  });
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.