Jak zmienić typ pola?

158

Próbuję zmienić typ pola z wnętrza powłoki mangusty.

Robię to ...

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

Ale to nie działa!

J. Quintas
źródło
Jeśli ktoś jest w tej samej sytuacji co ja, mając do czynienia toStringz polem dokumentu, oto mały program, który stworzyłem / użyłem .
podsumowuje

Odpowiedzi:

213

Jedynym sposobem zmiany $typedanych jest aktualizacja danych, które mają odpowiedni typ.

W tym przypadku wygląda na to, że próbujesz zmienić $type z 1 (double) na 2 (string) .

Po prostu załaduj dokument z bazy danych, wykonaj cast ( new String(x)), a następnie ponownie zapisz dokument.

Jeśli chcesz to zrobić programowo i całkowicie z poziomu powłoki, możesz użyć find(...).forEach(function(x) {})składni.


W odpowiedzi na drugi komentarz poniżej. Zmień pole badz liczby na ciąg w kolekcji foo.

db.foo.find( { 'bad' : { $type : 1 } } ).forEach( function (x) {   
  x.bad = new String(x.bad); // convert field to string
  db.foo.save(x);
});
Gates VP
źródło
1
Jakaś szansa na przykład - zmiana typu pola z int na string (lub odwrotnie), z powłoki?
Alister Bulman
30
w przypadku Int32-> String, new String(x.bad)tworzy kolekcję Strings z wartością 0-indeksu x.bad. Wariant ""+x.badopisany przez Simone działa zgodnie z życzeniem - tworzy wartość String zamiast Int32
Dao,
Powyższy kod konwertuje dane pola z double na tablicę zamiast double na string. Moje rzeczywiste dane były w tym formacie: 3.1, podczas gdy kod simone działa dobrze dla mnie
Pankaj Khurana
2
Miałem sytuację, w której musiałem przekonwertować pole _id, a także nie kolidować z innymi indeksami: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 tak, ponownie piszesz pole w swojej bazie danych, to wielka sprawa bez względu na to, jak to zrobisz. Gdybyś używał SQL i był to duży stół, prawdopodobnie musiałbyś trochę przestać.
Gates VP
161

Konwertuj pole typu String na liczbę całkowitą:

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

Konwertuj pole typu Integer na ciąg:

db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) {
    obj.field-name = "" + obj.field-name;
    db.db-name.save(obj);
});
Simone
źródło
To świetnie - czy wiesz, jak przekonwertować ciąg znaków (pomyśl o walucie typu „1,23”) na liczbę całkowitą 123? Zakładam, że musiałbyś przeanalizować to jako liczbę zmiennoprzecinkową lub dziesiętną, pomnożyć przez 100, a następnie zapisać jako liczbę całkowitą, ale nie mogę znaleźć odpowiednich dokumentów, aby to zrobić. Dzięki!
Brian Armstrong
Właściwie ta praca jest dobra. Ale mam aplikację działającą z mongoid 2.4.0-stable, która ma pola takie jak pole: customer_count, typ: Integer i walidacja jako validates_numericality_of: customer_count, który działał dobrze. Teraz, kiedy uaktualniam do mongoid do 3.0.16, kiedy przypisuję wartość ciągu, automatycznie konwertuje ją na 0. bez błędu. Chcę zgłosić błąd przy nieprawidłowym przypisaniu danych, to zachowanie okazuje się dla mnie dziwne.
Swapnil Chincholkar
4
Uruchomiłem to i otrzymałem błąd: Błąd: nie można przekonwertować ciągu znaków na liczbę całkowitą (powłoka): 1
Mittenchops
A jeśli chcesz przekonwertować ciąg znaków (lub „normalną” 32-bitową liczbę całkowitą) na 64-bitową liczbę całkowitą, użyj NumberLongjak tutaj: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
działa dobrze. Czy mogę wiedzieć, jak zmienić typ danych wewnątrz wbudowanych pól dokumentu
sankar Muniyappa
43

Dla konwersji ciągów na int.

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

Dla ciągów do podwójnej konwersji.

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

Dla pływaka:

    obj.my_value= parseFloat(obj.my_value);
David Dehghan
źródło
2
Poleciłbym również określić radix- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...
Russ Cam
5
Uważaj , przetestowałem to z Robomongo i zaowocowało to typem 1: double. Musiał użyćnew NumberInt()
Daniel F
Daniel, twoje rozwiązanie nie jest w porządku ... rozwiązanie
Davida
22
db.coll.find().forEach(function(data) {
    db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})
Russell
źródło
15

Zaczynając Mongo 4.2, db.collection.update()może akceptować potok agregacji, a na końcu zezwalając na aktualizację pola na podstawie jego własnej wartości:

// { 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" }
  • Pierwsza część { a : { $type: 1 } }to zapytanie dopasowujące:

    • Filtruje dokumenty do aktualizacji.
    • W tym przypadku, ponieważ chcemy przekonwertować "a"na łańcuch, gdy jego wartość jest double, dopasowuje to elementy, dla których "a"jest typu 1(double)).
    • Ta tabela zawiera kody reprezentujące różne możliwe typy.
  • Druga część [{ $set: { a: { $toString: "$a" } } }]to potok agregacji aktualizacji:

    • Zwróć uwagę na kwadratowe nawiasy wskazujące, że to zapytanie aktualizujące używa potoku agregacji.
    • $setjest nowym operatorem agregacji ( Mongo 4.2), który w tym przypadku modyfikuje pole.
    • Można to po prostu odczytać jako "$set"wartość "a"do "$a"konwersji "$toString".
    • Nowością jest możliwość Mongo 4.2odniesienia się do samego dokumentu podczas jego aktualizacji: nowa wartość dla "a"jest oparta na istniejącej wartości "$a".
    • Zwróć również uwagę, "$toString"który jest nowym operatorem agregacji wprowadzonym w Mongo 4.0.
  • Nie zapomnij { multi: true }, w przeciwnym razie zaktualizowany zostanie tylko pierwszy pasujący dokument.


W przypadku, gdy nie jest to odlew z double na łańcuch, masz wybór pomiędzy różnymi operatorami konwersji wprowadzonych Mongo 4.0takich jak $toBool, $toInt...

A jeśli nie jest dedykowany konwerter do docelowego typu, można wymienić { $toString: "$a" }z $convertpracy: { $convert: { input: "$a", to: 2 } }gdzie wartość tomożna znaleźć w tej tabeli :

db.collection.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $convert: { input: "$a", to: 2 } } } }],
  { multi: true }
)
Xavier Guihot
źródło
1
db.collection.updateMany( { a : { $type: 1 } }, [{ $set: { a: { $toString: "$a" } } }] )- multi : truemożna tego uniknąć używającupdateMany
Vasim
1
Od 2020 r. Użycie $ convert powinno być właściwą metodą, ponieważ powinno być znacznie bardziej wydajne (i łatwiejsze w użyciu do rozruchu).
KhalilRavanna
10

wszystkie dotychczasowe odpowiedzi używają jakiejś wersji forEach, iterując po wszystkich elementach kolekcji po stronie klienta.

Możesz jednak użyć przetwarzania po stronie serwera MongoDB, używając potoku agregacji i etapu $ out jako:

etap $ out atomowo zastępuje istniejącą kolekcję nową kolekcją wyników.

przykład:

db.documents.aggregate([
         {
            $project: {
               _id: 1,
               numberField: { $substr: ['$numberField', 0, -1] },
               otherField: 1,
               differentField: 1,
               anotherfield: 1,
               needolistAllFieldsHere: 1
            },
         },
         {
            $out: 'documents',
         },
      ]);
user3616725
źródło
2
Nie wiem, dlaczego nie jest to bardziej popierane. Operacje wiersz po wierszu na dużych zbiorach danych są morderstwem na wydajności
Alf47,
7

Aby przekonwertować pole typu string na pole Date , należałoby wykonać iterację kursora zwróconego przez find()metodę przy użyciu forEach()metody, w pętli przekonwertować pole na obiekt Date, a następnie zaktualizować pole za pomocą $setoperatora.

Skorzystaj z funkcji Bulk API do zbiorczych aktualizacji, które oferują lepszą wydajność, ponieważ będziesz wysyłać operacje na serwer partiami, powiedzmy 1000, co zapewnia lepszą wydajność, ponieważ nie wysyłasz każdego żądania do serwera, tylko raz na 1000 wniosków.

Poniższy przykład ilustruje to podejście, pierwszy przykład używa interfejsu Bulk API dostępnego w wersjach MongoDB >= 2.6 and < 3.2. Aktualizuje wszystkie dokumenty w kolekcji, zmieniając wszystkie created_atpola na pola daty:

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

Następny przykład dotyczy nowej wersji MongoDB, 3.2która od tego czasu wycofała Bulk API i udostępniła nowszy zestaw API przy użyciu 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 });
chridam
źródło
1
Świetna odpowiedź, metoda masowa działa dla mnie około 100 razy szybciej, mimo że nadal wydaje się być połączeniem synchronicznym.
Matthew Przeczytaj
3

Aby przekonwertować int32 na string w mongo bez tworzenia tablicy, po prostu dodaj "" do swojego numeru :-)

db.foo.find( { 'mynum' : { $type : 16 } } ).forEach( function (x) {   
  x.mynum = x.mynum + ""; // convert int32 to string
  db.foo.save(x);
});
Giulio Roggero
źródło
3

To, co naprawdę pomogło mi zmienić typ obiektu w MondoDB, to ta prosta linia, być może wspomniana wcześniej tutaj ...:

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

Użytkownicy to moja kolekcja, a wiek to obiekt, który miał ciąg znaków zamiast liczby całkowitej (int32).

Felipe
źródło
1

Muszę zmienić typ danych wielu pól w kolekcji, więc użyłem poniższego, aby dokonać wielu zmian typu danych w kolekcji dokumentów. Odpowiedz na stare pytanie, ale może być pomocne dla innych.

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); 
});
Aakash
źródło
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.

Spróbuj tego zapytania ...

db.collectionName.find().forEach( function (x) {
x.FieldName = parseInt(x.FieldName);
db.collectionName.save(x);
});
Amarendra Kumar
źródło
1

demo zmienia typ pola mid ze string na mongo objectId używając 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 to kolejny przykład takich stylów jak

Number, string, boolean, które mają nadzieję, że odpowiedź pomoże komuś innemu.

Tran Hoang Hiep
źródło
0

Używam tego skryptu w konsoli mongodb do ciągów znaków do konwersji typu float ...

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

A ten w 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)
    );


}
user3469031
źródło
0

w moim przypadku używam następujących

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}});
  });
}
Renish Gotecha
źródło