অন্য ক্ষেত্রের মান ব্যবহার করে মঙ্গোডিবি ক্ষেত্র আপডেট করুন


372

মঙ্গোডিবিতে, অন্য ক্ষেত্রের মান ব্যবহার করে কোনও ক্ষেত্রের মান আপডেট করা সম্ভব? সমতুল্য এসকিউএল এর মতো কিছু হবে:

UPDATE Person SET Name = FirstName + ' ' + LastName

এবং মঙ্গোডিবি সিউডো কোডটি হ'ল:

db.person.update( {}, { $set : { name : firstName + ' ' + lastName } );

উত্তর:


257

এই কাজ করতে সবচেয়ে ভালো উপায় সংস্করণ 4.2+ যা আপডেট ডকুমেন্ট এবং অ্যাগ্রিগেশন পাইপলাইন ব্যবহার করতে পারবেন হয় updateOne, updateManyবা updateসংগ্রহ পদ্ধতি। মনে রাখবেন যে সমস্ত ভাষা চালক না থাকলে পরবর্তীকালে বেশিরভাগ ক্ষেত্রেই অবচয় করা হয়েছে।

মঙ্গোডিবি 4.2+

সংস্করণ ৪.২ $setপাইপলাইন মঞ্চ অপারেটরটিও প্রবর্তন করে যা এর জন্য একটি উপনাম $addFields। আমরা যা অর্জন করার চেষ্টা করছি তার মানচিত্র$set হিসাবে আমি এখানে ব্যবহার করব ।

db.collection.<update method>(
    {},
    [
        {"$set": {"name": { "$concat": ["$firstName", " ", "$lastName"]}}}
    ]
)

মঙ্গোডিবি 3.4+

3.4+ এ আপনি $addFieldsএবং $outসমষ্টি পাইপলাইন অপারেটরগুলি ব্যবহার করতে পারেন ।

db.collection.aggregate(
    [
        { "$addFields": { 
            "name": { "$concat": [ "$firstName", " ", "$lastName" ] } 
        }},
        { "$out": "collection" }
    ]
)

মনে রাখবেন এটি আপনার সংগ্রহ আপডেট করে না বরং পরিবর্তে বিদ্যমান সংগ্রহটি প্রতিস্থাপন করে বা একটি নতুন তৈরি করে। এছাড়াও আপডেট অপারেশনের জন্য যা "টাইপ কাস্টিং" প্রয়োজন আপনার ক্লায়েন্ট সাইড প্রসেসিং প্রয়োজন হবে এবং অপারেশনের উপর নির্ভর করে আপনাকে find()পদ্ধতির পরিবর্তে পদ্ধতিটি ব্যবহার করতে হতে পারে .aggreate()

মঙ্গোডিবি 3.2 এবং 3.0

আমরা যেভাবে এটি করি তা হ'ল $projectআমাদের দস্তাবেজগুলিকে আইং করে এবং $concatস্ট্রিং অগ্রিগেশন অপারেটরটি সংক্ষিপ্ত স্ট্রিংটি ফিরিয়ে আনার জন্য। আমরা সেখান থেকে, আপনি তারপরে কার্সারটি পুনরায় শুরু করুন এবং সর্বাধিক দক্ষতার জন্য বাল্ক অপারেশনগুলি$set ব্যবহার করে আপনার দস্তাবেজে নতুন ক্ষেত্রটি যুক্ত করতে আপডেট অপারেটরটি ব্যবহার করুন ।

সমষ্টি কোয়েরি:

var cursor = db.collection.aggregate([ 
    { "$project":  { 
        "name": { "$concat": [ "$firstName", " ", "$lastName" ] } 
    }}
])

মঙ্গোডিবি 3.2 বা আরও নতুন

এটি থেকে আপনার bulkWriteপদ্ধতিটি ব্যবহার করা উচিত ।

var requests = [];
cursor.forEach(document => { 
    requests.push( { 
        'updateOne': {
            'filter': { '_id': document._id },
            'update': { '$set': { 'name': document.name } }
        }
    });
    if (requests.length === 500) {
        //Execute per 500 operations and re-init
        db.collection.bulkWrite(requests);
        requests = [];
    }
});

if(requests.length > 0) {
     db.collection.bulkWrite(requests);
}

মঙ্গোডিবি 2.6 এবং 3.0

এই সংস্করণ থেকে আপনার এখন অবহেলিত Bulkএপিআই এবং এর সম্পর্কিত পদ্ধতিগুলি ব্যবহার করা দরকার

var bulk = db.collection.initializeUnorderedBulkOp();
var count = 0;

cursor.snapshot().forEach(function(document) { 
    bulk.find({ '_id': document._id }).updateOne( {
        '$set': { 'name': document.name }
    });
    count++;
    if(count%500 === 0) {
        // Excecute per 500 operations and re-init
        bulk.execute();
        bulk = db.collection.initializeUnorderedBulkOp();
    }
})

// clean up queues
if(count > 0) {
    bulk.execute();
}

মঙ্গোডিবি 2.4

cursor["result"].forEach(function(document) {
    db.collection.update(
        { "_id": document._id }, 
        { "$set": { "name": document.name } }
    );
})

আমি মনে করি "মঙ্গোডিবি 3.2 বা আরও নতুন" এর কোডটিতে কোনও সমস্যা আছে। যেহেতু প্রত্যেকটি অ্যাসিঙ্ক হয় তাই সাধারণত শেষ বাল্ক রাইটে কিছুই লেখা হয় না।
ভিক্টর হেডেফালক

3
4.2+ কাজ করে না। মঙ্গোএরর: ডলারের ($) প্রিফিক্স ফিল্ড '$ কনক্যাট' নামে 'name কনক্যাট' স্টোরেজটির জন্য বৈধ নয়।
জোশ উডকক

@ জোশওয়াডকক, আমার মনে হয় আপনি যে ক্যোয়ারী চালাচ্ছেন তাতে টাইপস পড়েছিল। আমি আপনাকে ডাবল চেক পরামর্শ দিচ্ছি।
স্টাইভেন

@ জোশওয়াডকক এটি সুন্দরভাবে কাজ করে। দয়া করে এটি
মংগোডিবি

2
@ জোডউডকক বর্ণিত একই সমস্যাটিতে যারা চলেছেন তাদের জন্য: মনোযোগ দিন যে 4.2+ এর উত্তর একটি সমষ্টি পাইপলাইন বর্ণনা করে , তাই দ্বিতীয় প্যারামিটারে বর্গাকার বন্ধনীগুলি মিস করবেন না !
ফিলস্চ

240

আপনার মাধ্যমে পুনরাবৃত্তি করা উচিত। আপনার নির্দিষ্ট ক্ষেত্রে:

db.person.find().snapshot().forEach(
    function (elem) {
        db.person.update(
            {
                _id: elem._id
            },
            {
                $set: {
                    name: elem.firstname + ' ' + elem.lastname
                }
            }
        );
    }
);

4
যদি অন্য কোনও ব্যবহারকারী আপনার অনুসন্ধান () এবং আপনার সংরক্ষণ () এর মধ্যে ডকুমেন্ট পরিবর্তন করে থাকে তবে কী ঘটে?
দ্য ক্রিক

3
সত্য, তবে ক্ষেত্রগুলির মধ্যে অনুলিপি করার জন্য আণবিক হতে হবে না be
আপক্রিক

3
save()দস্তাবেজের পুরোপুরি প্রতিস্থাপন করা এটি লক্ষ্য করা গুরুত্বপূর্ণ । update()পরিবর্তে ব্যবহার করা উচিত।
কার্লোস

12
কীভাবেdb.person.update( { _id: elem._id }, { $set: { name: elem.firstname + ' ' + elem.lastname } } );
ফিলিপ জর্দাস

1
আমি একটি ফাংশন তৈরি করেছি create_guidযা ডকুমেন্ট প্রতি কেবল একটি অনন্য গাইড তৈরি করে যখন forEachএইভাবে পুনরাবৃত্তি করে (অর্থাত্ create_guidএকটি updateবিবৃতিতে কেবল mutli=trueএকই নথির জন্য সমস্ত নথির জন্য একই নির্দেশিকা উত্পন্ন করার কারণ হয়)। এই উত্তরটি আমার পক্ষে পুরোপুরি কার্যকর হয়েছিল worked +1
rmirabelle

103

স্পষ্টতই মোংগোডিবি ৩.৪ থেকে কার্যকরভাবে এটি করার একটি উপায় রয়েছে, স্টাইভেনের উত্তর দেখুন


নীচে অপ্রচলিত উত্তর

আপনি কোনও আপডেটে ডকুমেন্টটি নিজেই উল্লেখ করতে পারবেন না (এখনও)। আপনাকে ডকুমেন্টগুলির মাধ্যমে পুনরাবৃত্তি করতে হবে এবং একটি ফাংশন ব্যবহার করে প্রতিটি দস্তাবেজ আপডেট করতে হবে। উদাহরণস্বরূপ এই উত্তরটি দেখুন বা এটি সার্ভার-সাইডের জন্য দেখুন eval()


31
এটি কি আজও বৈধ?
খ্রিস্টান এঙ্গেল

3
@ ক্রিশ্চিয়ান এঞ্জেল: এটি তাই দেখা যায়। মোঙ্গোডিবি ডক্সে আমি এমন কোনও কিছুই খুঁজে পাইনি যেটিতে একটি updateঅপারেশনে বর্তমান নথির উল্লেখ উল্লেখ রয়েছে । এই সম্পর্কিত বৈশিষ্ট্য অনুরোধটি এখনও অমীমাংসিত।
নিলস ভ্যান ডের রেস্ট 12

4
এটি এখনও এপ্রিল 2017 সালে বৈধ? বা ইতিমধ্যে নতুন বৈশিষ্ট্য রয়েছে যা এটি করতে পারে?
কিম

1
@ কিম দেখে মনে হচ্ছে এটি এখনও বৈধ। এছাড়াও, বৈশিষ্ট্যটির জন্য অনুরোধ করা হয়েছে যে @ নীল-ভ্যান-ডের-রেস্ট ২০১৩ সালে ফিরে নির্দেশিত এখনও রয়েছে OPEN
ডানজিগার

8
এটি আর বৈধ উত্তর নয়, @ স্টাইভেন উত্তরটি দেখুন
আইচখান

45

উচ্চ ক্রিয়াকলাপ সহ একটি ডেটাবেসের জন্য, আপনি আপডেট করতে পারেন যেখানে আপনার আপডেটগুলি সক্রিয়ভাবে রেকর্ড পরিবর্তন করতে প্রভাবিত করে এবং এই কারণে আমি স্ন্যাপশট ব্যবহারের প্রস্তাব দিই ()

db.person.find().snapshot().forEach( function (hombre) {
    hombre.name = hombre.firstName + ' ' + hombre.lastName; 
    db.person.save(hombre); 
});

http://docs.mongodb.org/manual/reference/method/cursor.snapshot/


2
যদি অন্য ব্যবহারকারী ব্যক্তিটিকে অনুসন্ধান () এবং সংরক্ষণ () এর মধ্যে সম্পাদনা করে তবে কী হবে? আমার একটি কেস রয়েছে যেখানে তাদের বর্তমান মানগুলির উপর ভিত্তি করে একই জিনিসকে পরিবর্তন করে একাধিক কল করা যেতে পারে। ২ য় ব্যবহারকারীর সংরক্ষণের সাথে 1 ম সম্পন্ন না হওয়া পর্যন্ত পড়া সহ অপেক্ষা করা উচিত। এটি কি এটি সম্পাদন করে?
মার্কো

4
সম্পর্কে snapshot(): Deprecated in the mongo Shell since v3.2. Starting in v3.2, the $snapshot operator is deprecated in the mongo shell. In the mongo shell, use cursor.snapshot() instead. লিঙ্ক
পাইপথন

10

এই উত্তরটির বিষয়ে , এই আপডেট অনুসারে স্ন্যাপশট ফাংশনটি 3.6 সংস্করণে অবচিত করা হয়েছে । সুতরাং, সংস্করণে 3.6 এবং তারপরে, এই পদ্ধতিটি অপারেশন করা সম্ভব:

db.person.find().forEach(
    function (elem) {
        db.person.update(
            {
                _id: elem._id
            },
            {
                $set: {
                    name: elem.firstname + ' ' + elem.lastname
                }
            }
        );
    }
);

8

আমি উপরের সমাধানটি চেষ্টা করেছিলাম তবে আমি এটি প্রচুর পরিমাণে ডেটার জন্য অনুপযুক্ত বলে মনে করি। আমি তখন স্ট্রিম বৈশিষ্ট্যটি আবিষ্কার করেছি:

MongoClient.connect("...", function(err, db){
    var c = db.collection('yourCollection');
    var s = c.find({/* your query */}).stream();
    s.on('data', function(doc){
        c.update({_id: doc._id}, {$set: {name : doc.firstName + ' ' + doc.lastName}}, function(err, result) { /* result == true? */} }
    });
    s.on('end', function(){
        // stream can end before all your updates do if you have a lot
    })
})

1
এটি কীভাবে আলাদা? আপডেটের ক্রিয়াকলাপটি দিয়ে বাষ্পটি থ্রটল হবে? আপনার এটার কোনও রেফারেন্স আছে? মঙ্গো ডক্সটি বেশ দুর্বল।
নিকো

8

শুরু করা Mongo 4.2, db.collection.update()শেষ পর্যন্ত অন্য ক্ষেত্রের উপর ভিত্তি করে কোনও ক্ষেত্রের হালনাগাদ / নির্মাণের অনুমতি প্রদান করে একত্রিত পাইপলাইন গ্রহণ করতে পারে:

// { firstName: "Hello", lastName: "World" }
db.collection.update(
  {},
  [{ $set: { name: { $concat: [ "$firstName", " ", "$lastName" ] } } }],
  { multi: true }
)
// { "firstName" : "Hello", "lastName" : "World", "name" : "Hello World" }
  • প্রথম অংশটি {}ম্যাচ ক্যোয়ারী, কোন নথিগুলি আপডেট করতে হবে তা ফিল্টার করে (আমাদের ক্ষেত্রে সমস্ত নথি)।

  • দ্বিতীয় অংশটি [{ $set: { name: { ... } }]হ'ল আপডেট একীকরণ পাইপলাইন (একত্রিত পাইপলাইন ব্যবহারের জন্য নির্দেশিত স্কোয়ার বন্ধনীগুলি নোট করুন)। $setএকটি নতুন অ্যাগ্রিগেশন অপারেটর এবং উপনাম হল $addFields

  • ভুলে যাবেন না { multi: true }, অন্যথায় কেবল প্রথম ম্যাচের ডকুমেন্ট আপডেট করা হবে।


2

Field 150_000 রেকর্ডের জন্য আমরা অন্য এক ক্ষেত্রের অনুলিপি করার জন্য এখানে কী নিয়ে এসেছি। এটি প্রায় 6 মিনিট সময় নিয়েছে তবে একই সংখ্যক রুবি অবজেক্টের উপর এটি ইনস্ট্যান্টিয়েট এবং পুনরুক্তকরণের চেয়ে তাত্পর্যপূর্ণভাবে কম সংস্থান নিচ্ছে।

js_query = %({
  $or : [
    {
      'settings.mobile_notifications' : { $exists : false },
      'settings.mobile_admin_notifications' : { $exists : false }
    }
  ]
})

js_for_each = %(function(user) {
  if (!user.settings.hasOwnProperty('mobile_notifications')) {
    user.settings.mobile_notifications = user.settings.email_notifications;
  }
  if (!user.settings.hasOwnProperty('mobile_admin_notifications')) {
    user.settings.mobile_admin_notifications = user.settings.email_admin_notifications;
  }
  db.users.save(user);
})

js = "db.users.find(#{js_query}).forEach(#{js_for_each});"
Mongoid::Sessions.default.command('$eval' => js)

1

সঙ্গে MongoDB সংস্করণ 4.2+ , আপডেট নমনীয় যেমন তার মধ্যে অ্যাগ্রিগেশন পাইপলাইন ব্যবহার করতে পারবে হয় update, updateOneএবং updateMany। আপনি এখন একীকরণ অপারেটরগুলি ব্যবহার করে আপনার দস্তাবেজগুলিকে রূপান্তর করতে পারেন তারপরে $setকমান্ডটি স্পষ্ট করার প্রয়োজন ছাড়াই আপডেট করুন (পরিবর্তে আমরা ব্যবহার করি$replaceRoot: {newRoot: "$$ROOT"} )

এখানে আমরা মঙ্গোডিবি'র অবজেক্টআইডি "_আইডি" ক্ষেত্রটি থেকে টাইমস্ট্যাম্পটি বের করতে এবং ডকুমেন্টগুলি আপডেট করতে সামগ্রিক ক্যোয়ারী ব্যবহার করি (আমি এসকিউএল তে বিশেষজ্ঞ নই তবে আমার মনে হয় এসকিউএল কোনও টাইমস্ট্যাম্পযুক্ত অটো জেনারেট অবজেক্টইড সরবরাহ করে না, আপনাকে অবশ্যই স্বয়ংক্রিয়ভাবে সেই তারিখটি তৈরি করুন)

var collection = "person"

agg_query = [
    {
        "$addFields" : {
            "_last_updated" : {
                "$toDate" : "$_id"
            }
        }
    },
    {
        $replaceRoot: {
            newRoot: "$$ROOT"
        } 
    }
]

db.getCollection(collection).updateMany({}, agg_query, {upsert: true})

আপনার দরকার নেই { $replaceRoot: { newRoot: "$$ROOT" } }; এর অর্থ ডকুমেন্টটি নিজেই প্রতিস্থাপন করা অর্থহীন। তোমাদের স্থলাভিষিক্ত তাহলে $addFieldsতার ওরফে দ্বারা $setএবং updateManyযার জন্য alias লেখা এক update, তাহলে আপনি যেমন সঠিক একই উত্তর পেতে এই এক উপরে।
জাভিয়র গুইহট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.