आपको session
. शामिल करना होगा लेन-देन के दौरान सक्रिय सभी पढ़ने/लिखने के संचालन के विकल्पों के भीतर। केवल तभी वे वास्तव में लेन-देन के दायरे में लागू होते हैं जहां आप उन्हें वापस रोल करने में सक्षम होते हैं।
थोड़ी अधिक संपूर्ण सूची के रूप में, और बस अधिक क्लासिक Order/OrderItems
. का उपयोग करना मॉडलिंग जो कुछ संबंधपरक लेनदेन अनुभव वाले अधिकांश लोगों के लिए काफी परिचित होना चाहिए:
const { Schema } = mongoose = require('mongoose');
// URI including the name of the replicaSet connecting to
const uri = 'mongodb://localhost:27017/trandemo?replicaSet=fresh';
const opts = { useNewUrlParser: true };
// sensible defaults
mongoose.Promise = global.Promise;
mongoose.set('debug', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
// schema defs
const orderSchema = new Schema({
name: String
});
const orderItemsSchema = new Schema({
order: { type: Schema.Types.ObjectId, ref: 'Order' },
itemName: String,
price: Number
});
const Order = mongoose.model('Order', orderSchema);
const OrderItems = mongoose.model('OrderItems', orderItemsSchema);
// log helper
const log = data => console.log(JSON.stringify(data, undefined, 2));
// main
(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// clean models
await Promise.all(
Object.entries(conn.models).map(([k,m]) => m.deleteMany())
)
let session = await conn.startSession();
session.startTransaction();
// Collections must exist in transactions
await Promise.all(
Object.entries(conn.models).map(([k,m]) => m.createCollection())
);
let [order, other] = await Order.insertMany([
{ name: 'Bill' },
{ name: 'Ted' }
], { session });
let fred = new Order({ name: 'Fred' });
await fred.save({ session });
let items = await OrderItems.insertMany(
[
{ order: order._id, itemName: 'Cheese', price: 1 },
{ order: order._id, itemName: 'Bread', price: 2 },
{ order: order._id, itemName: 'Milk', price: 3 }
],
{ session }
);
// update an item
let result1 = await OrderItems.updateOne(
{ order: order._id, itemName: 'Milk' },
{ $inc: { price: 1 } },
{ session }
);
log(result1);
// commit
await session.commitTransaction();
// start another
session.startTransaction();
// Update and abort
let result2 = await OrderItems.findOneAndUpdate(
{ order: order._id, itemName: 'Milk' },
{ $inc: { price: 1 } },
{ 'new': true, session }
);
log(result2);
await session.abortTransaction();
/*
* $lookup join - expect Milk to be price: 4
*
*/
let joined = await Order.aggregate([
{ '$match': { _id: order._id } },
{ '$lookup': {
'from': OrderItems.collection.name,
'foreignField': 'order',
'localField': '_id',
'as': 'orderitems'
}}
]);
log(joined);
} catch(e) {
console.error(e)
} finally {
mongoose.disconnect()
}
})()
इसलिए मैं आमतौर पर वेरिएबल session
. को कॉल करने की सलाह दूंगा लोअरकेस में, चूंकि यह "विकल्प" ऑब्जेक्ट के लिए कुंजी का नाम है जहां सभी परिचालनों पर इसकी आवश्यकता होती है। इसे लोअरकेस कन्वेंशन में रखने से ES6 ऑब्जेक्ट असाइनमेंट जैसी चीज़ों का भी उपयोग करने की अनुमति मिलती है:
const conn = await mongoose.connect(uri, opts);
...
let session = await conn.startSession();
session.startTransaction();
इसके अलावा लेन-देन पर नेवला दस्तावेज थोड़ा भ्रामक है, या कम से कम यह अधिक वर्णनात्मक हो सकता है। इसे db
के रूप में संदर्भित किया जाता है उदाहरणों में वास्तव में नेवला कनेक्शन उदाहरण है, न कि अंतर्निहित Db
या यहां तक कि mongoose
वैश्विक आयात के रूप में कुछ इसकी गलत व्याख्या कर सकते हैं। लिस्टिंग और ऊपर के अंश में नोट यह mongoose.connect()
. से प्राप्त किया गया है और इसे आपके कोड में रखा जाना चाहिए, जिसे आप किसी साझा आयात से एक्सेस कर सकते हैं।
वैकल्पिक रूप से आप इसे mongoose.connection
. के माध्यम से मॉड्यूलर कोड में भी प्राप्त कर सकते हैं संपत्ति, किसी भी समय बाद एक कनेक्शन स्थापित किया गया है। यह आमतौर पर सर्वर रूट हैंडलर जैसी चीजों के अंदर सुरक्षित होता है और जैसे कोड को कॉल किए जाने तक डेटाबेस कनेक्शन होगा।
कोड session
. को भी प्रदर्शित करता है विभिन्न मॉडल विधियों में उपयोग:
let [order, other] = await Order.insertMany([
{ name: 'Bill' },
{ name: 'Ted' }
], { session });
let fred = new Order({ name: 'Fred' });
await fred.save({ session });
सभी find()
आधारित तरीके और update()
या insert()
और delete()
आधारित विधियों में सभी अंतिम "विकल्प ब्लॉक" होते हैं जहां इस सत्र कुंजी और मान की अपेक्षा की जाती है। save()
विधि का एकमात्र तर्क यह विकल्प ब्लॉक है। यही वह है जो MongoDB को इन क्रियाओं को उस संदर्भित सत्र में वर्तमान लेनदेन पर लागू करने के लिए कहता है।
ठीक उसी तरह, लेन-देन करने से पहले find()
. के लिए कोई भी अनुरोध किया जाता है या समान जो उस session
को निर्दिष्ट नहीं करते हैं विकल्प डेटा की स्थिति नहीं देखता है, जबकि लेनदेन प्रगति पर है। लेन-देन पूरा होने के बाद संशोधित डेटा स्थिति केवल अन्य कार्यों के लिए उपलब्ध है। ध्यान दें कि दस्तावेज़ीकरण में शामिल लेखन पर इसका प्रभाव पड़ता है।
जब एक "निरस्त" जारी किया जाता है:
// Update and abort
let result2 = await OrderItems.findOneAndUpdate(
{ order: order._id, itemName: 'Milk' },
{ $inc: { price: 1 } },
{ 'new': true, session }
);
log(result2);
await session.abortTransaction();
सक्रिय लेनदेन पर कोई भी कार्रवाई राज्य से हटा दी जाती है और लागू नहीं होती है। जैसे वे बाद में परिणामी संचालन के लिए दृश्यमान नहीं हैं। उदाहरण में यहां दस्तावेज़ में मान बढ़ा हुआ है और 5
. का पुनर्प्राप्त मान दिखाएगा वर्तमान सत्र पर। हालांकि session.abortTransaction()
. के बाद दस्तावेज़ की पिछली स्थिति को वापस कर दिया गया है। ध्यान दें कि कोई भी वैश्विक संदर्भ जो एक ही सत्र में डेटा नहीं पढ़ रहा था, उस स्थिति को तब तक नहीं बदलता जब तक कि प्रतिबद्ध न हो।
यह सामान्य अवलोकन देना चाहिए। लेखन विफलता और पुन:प्रयास के विभिन्न स्तरों को संभालने के लिए और अधिक जटिलताएं जोड़ी जा सकती हैं, लेकिन यह पहले से ही दस्तावेज़ीकरण और कई नमूनों में व्यापक रूप से शामिल है, या अधिक विशिष्ट प्रश्न का उत्तर दिया जा सकता है।
आउटपुट
संदर्भ के लिए, शामिल लिस्टिंग का आउटपुट यहां दिखाया गया है:
Mongoose: orders.deleteMany({}, {})
Mongoose: orderitems.deleteMany({}, {})
Mongoose: orders.insertMany([ { _id: 5bf775986c7c1a61d12137dd, name: 'Bill', __v: 0 }, { _id: 5bf775986c7c1a61d12137de, name: 'Ted', __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
Mongoose: orders.insertOne({ _id: ObjectId("5bf775986c7c1a61d12137df"), name: 'Fred', __v: 0 }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
Mongoose: orderitems.insertMany([ { _id: 5bf775986c7c1a61d12137e0, order: 5bf775986c7c1a61d12137dd, itemName: 'Cheese', price: 1, __v: 0 }, { _id: 5bf775986c7c1a61d12137e1, order: 5bf775986c7c1a61d12137dd, itemName: 'Bread', price: 2, __v: 0 }, { _id: 5bf775986c7c1a61d12137e2, order: 5bf775986c7c1a61d12137dd, itemName: 'Milk', price: 3, __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
Mongoose: orderitems.updateOne({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
{
"n": 1,
"nModified": 1,
"opTime": {
"ts": "6626894672394452998",
"t": 139
},
"electionId": "7fffffff000000000000008b",
"ok": 1,
"operationTime": "6626894672394452998",
"$clusterTime": {
"clusterTime": "6626894672394452998",
"signature": {
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId": 0
}
}
}
Mongoose: orderitems.findOneAndUpdate({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2"), upsert: false, remove: false, projection: {}, returnOriginal: false })
{
"_id": "5bf775986c7c1a61d12137e2",
"order": "5bf775986c7c1a61d12137dd",
"itemName": "Milk",
"price": 5,
"__v": 0
}
Mongoose: orders.aggregate([ { '$match': { _id: 5bf775986c7c1a61d12137dd } }, { '$lookup': { from: 'orderitems', foreignField: 'order', localField: '_id', as: 'orderitems' } } ], {})
[
{
"_id": "5bf775986c7c1a61d12137dd",
"name": "Bill",
"__v": 0,
"orderitems": [
{
"_id": "5bf775986c7c1a61d12137e0",
"order": "5bf775986c7c1a61d12137dd",
"itemName": "Cheese",
"price": 1,
"__v": 0
},
{
"_id": "5bf775986c7c1a61d12137e1",
"order": "5bf775986c7c1a61d12137dd",
"itemName": "Bread",
"price": 2,
"__v": 0
},
{
"_id": "5bf775986c7c1a61d12137e2",
"order": "5bf775986c7c1a61d12137dd",
"itemName": "Milk",
"price": 4,
"__v": 0
}
]
}
]