वास्तव में, मोंगोडीबी "डिफ़ॉल्ट" द्वारा डुप्लिकेट डेटा नहीं बनाएगा जहां एक "अद्वितीय कुंजी" शामिल है, जिसमें से _id
( नेवला द्वारा id
. के रूप में उपनाम दिया गया है , लेकिन insertMany()
. द्वारा अनदेखा किया गया इसलिए आपको सावधान रहने की आवश्यकता है), लेकिन इससे कहीं अधिक बड़ी कहानी है जिसके बारे में आपको वास्तव में जागरूक होने की आवश्यकता है ।
यहां मूल समस्या यह है कि insertMany()
. के दोनों "नेवला" कार्यान्वयन साथ ही अंतर्निहित ड्राइवर वर्तमान में इसे हल्के ढंग से रखने के लिए थोड़ा "बोर्क" कर रहे हैं। चूंकि ड्राइवर "थोक" संचालन में त्रुटि प्रतिक्रिया कैसे पास करता है, इसमें थोड़ा सा असंगतता है और यह वास्तव में "मोंगोज़" द्वारा वास्तविक त्रुटि जानकारी के लिए वास्तव में "सही जगह पर नहीं दिख रहा है" द्वारा जटिल है।
आप जिस "त्वरित" भाग को याद कर रहे हैं वह { ordered: false }
. का जोड़ है "बल्क" ऑपरेशन के लिए जिसमें .insertMany()
बस एक कॉल को लपेटता है। इसे सेट करने से यह सुनिश्चित होता है कि अनुरोधों का "बैच" वास्तव में "पूरी तरह से" सबमिट किया गया है और त्रुटि होने पर निष्पादन को नहीं रोकता है।
लेकिन चूंकि "मोंगोज़" इसे बहुत अच्छी तरह से संभाल नहीं पाता है (न ही ड्राइवर "लगातार" है) हमें वास्तव में अंतर्निहित कॉलबैक के "त्रुटि" परिणाम के बजाय "प्रतिक्रिया" में संभावित "त्रुटियों" की तलाश करने की आवश्यकता है।
एक प्रदर्शन के रूप में:
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const uri = 'mongodb://localhost/test',
options = { useMongoClient: true };
const songSchema = new Schema({
_id: Number,
name: String
});
const Song = mongoose.model('Song', songSchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
let docs = [
{ _id: 1, name: "something" },
{ _id: 2, name: "something else" },
{ _id: 2, name: "something else entirely" },
{ _id: 3, name: "another thing" }
];
mongoose.connect(uri,options)
.then( () => Song.remove() )
.then( () =>
new Promise((resolve,reject) =>
Song.collection.insertMany(docs,{ ordered: false },function(err,result) {
if (result.hasWriteErrors()) {
// Log something just for the sake of it
console.log('Has Write Errors:');
log(result.getWriteErrors());
// Check to see if something else other than a duplicate key, and throw
if (result.getWriteErrors().some( error => error.code != 11000 ))
reject(err);
}
resolve(result); // Otherwise resolve
})
)
)
.then( results => { log(results); return true; } )
.then( () => Song.find() )
.then( songs => { log(songs); mongoose.disconnect() })
.catch( err => { console.error(err); mongoose.disconnect(); } );
या शायद थोड़ा अच्छा है क्योंकि वर्तमान एलटीएस नोड.जेएस में async/await
है :
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const uri = 'mongodb://localhost/test',
options = { useMongoClient: true };
const songSchema = new Schema({
_id: Number,
name: String
});
const Song = mongoose.model('Song', songSchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
let docs = [
{ _id: 1, name: "something" },
{ _id: 2, name: "something else" },
{ _id: 2, name: "something else entirely" },
{ _id: 3, name: "another thing" }
];
(async function() {
try {
const conn = await mongoose.connect(uri,options);
await Song.remove();
let results = await new Promise((resolve,reject) => {
Song.collection.insertMany(docs,{ ordered: false },function(err,result) {
if (result.hasWriteErrors()) {
// Log something just for the sake of it
console.log('Has Write Errors:');
log(result.getWriteErrors());
// Check to see if something else other than a duplicate key, then throw
if (result.getWriteErrors().some( error => error.code != 11000 ))
reject(err);
}
resolve(result); // Otherwise resolve
});
});
log(results);
let songs = await Song.find();
log(songs);
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
किसी भी दर पर, आपको वही परिणाम मिलता है जो दर्शाता है कि लेखन दोनों जारी हैं और हम सम्मानपूर्वक "डुप्लिकेट कुंजी" से संबंधित त्रुटियों को "अनदेखा" करते हैं या अन्यथा त्रुटि कोड 11000
के रूप में जाना जाता है। . "सुरक्षित हैंडलिंग" यह है कि हम ऐसी त्रुटियों की अपेक्षा करते हैं और "अन्य त्रुटियों" की उपस्थिति की तलाश में उन्हें त्याग देते हैं, जिन पर हम केवल ध्यान देना चाहते हैं। हम यह भी देखते हैं कि शेष कोड जारी है और बाद में .find()
निष्पादित करके वास्तव में डाले गए सभी दस्तावेज़ों को सूचीबद्ध करता है। कॉल करें:
Mongoose: songs.remove({}, {})
Mongoose: songs.insertMany([ { _id: 1, name: 'something' }, { _id: 2, name: 'something else' }, { _id: 2, name: 'something else entirely' }, { _id: 3, name: 'another thing' } ], { ordered: false })
Has Write Errors:
[
{
"code": 11000,
"index": 2,
"errmsg": "E11000 duplicate key error collection: test.songs index: _id_ dup key: { : 2 }",
"op": {
"_id": 2,
"name": "something else entirely"
}
}
]
{
"ok": 1,
"writeErrors": [
{
"code": 11000,
"index": 2,
"errmsg": "E11000 duplicate key error collection: test.songs index: _id_ dup key: { : 2 }",
"op": {
"_id": 2,
"name": "something else entirely"
}
}
],
"writeConcernErrors": [],
"insertedIds": [
{
"index": 0,
"_id": 1
},
{
"index": 1,
"_id": 2
},
{
"index": 2,
"_id": 2
},
{
"index": 3,
"_id": 3
}
],
"nInserted": 3,
"nUpserted": 0,
"nMatched": 0,
"nModified": 0,
"nRemoved": 0,
"upserted": [],
"lastOp": {
"ts": "6485492726828630028",
"t": 23
}
}
Mongoose: songs.find({}, { fields: {} })
[
{
"_id": 1,
"name": "something"
},
{
"_id": 2,
"name": "something else"
},
{
"_id": 3,
"name": "another thing"
}
]
तो यह प्रक्रिया क्यों? इसका कारण यह है कि अंतर्निहित कॉल वास्तव में err
. दोनों लौटाती है और result
जैसा कि कॉलबैक कार्यान्वयन में दिखाया गया है, लेकिन जो लौटाया गया है उसमें एक असंगति है। ऐसा करने का मुख्य कारण यह है कि आप वास्तव में "परिणाम" देखते हैं, जिसमें न केवल सफल संचालन का परिणाम होता है, बल्कि त्रुटि संदेश भी होता है।
त्रुटि जानकारी के साथ nInserted: 3
. है यह दर्शाता है कि "बैच" में से कितने वास्तव में लिखे गए थे। आप insertedIds
. को काफी हद तक अनदेखा कर सकते हैं यहाँ क्योंकि इस विशेष परीक्षण में वास्तव में _id
. की आपूर्ति शामिल है मूल्य। उस घटना में जहां एक अलग संपत्ति में "अद्वितीय" बाधा थी जो त्रुटि का कारण बनती है, तो यहां केवल वही मान होंगे जो वास्तविक सफल लेखन से होंगे। थोड़ा भ्रामक, लेकिन परीक्षण करना और स्वयं देखना आसान है।
जैसा कि कहा गया है, पकड़ "असंगतता" है जिसे एक अन्य उदाहरण के साथ प्रदर्शित किया जा सकता है ( async/await
केवल लिस्टिंग की संक्षिप्तता के लिए):
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const uri = 'mongodb://localhost/test',
options = { useMongoClient: true };
const songSchema = new Schema({
_id: Number,
name: String
});
const Song = mongoose.model('Song', songSchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
let docs = [
{ _id: 1, name: "something" },
{ _id: 2, name: "something else" },
{ _id: 2, name: "something else entirely" },
{ _id: 3, name: "another thing" },
{ _id: 4, name: "different thing" },
//{ _id: 4, name: "different thing again" }
];
(async function() {
try {
const conn = await mongoose.connect(uri,options);
await Song.remove();
try {
let results = await Song.insertMany(docs,{ ordered: false });
console.log('what? no result!');
log(results); // not going to get here
} catch(e) {
// Log something for the sake of it
console.log('Has write Errors:');
// Check to see if something else other than a duplicate key, then throw
// Branching because MongoError is not consistent
if (e.hasOwnProperty('writeErrors')) {
log(e.writeErrors);
if(e.writeErrors.some( error => error.code !== 11000 ))
throw e;
} else if (e.code !== 11000) {
throw e;
} else {
log(e);
}
}
let songs = await Song.find();
log(songs);
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
सब कुछ एक जैसा ही है, लेकिन ध्यान दें कि त्रुटि यहाँ कैसे लॉग होती है:
Has write Errors:
{
"code": 11000,
"index": 2,
"errmsg": "E11000 duplicate key error collection: test.songs index: _id_ dup key: { : 2 }",
"op": {
"__v": 0,
"_id": 2,
"name": "something else entirely"
}
}
ध्यान दें कि कोई "सफलता" जानकारी नहीं है, भले ही हम बाद में .find()
करके लिस्टिंग की समान निरंतरता प्राप्त करते हैं। और आउटपुट प्राप्त करना। ऐसा इसलिए है क्योंकि कार्यान्वयन केवल अस्वीकृति में "फेंकने में त्रुटि" पर कार्य करता है और वास्तविक result
से कभी नहीं गुजरता है अंश। तो भले ही हमने ordered: false
, जब तक हम कॉलबैक को लपेटते नहीं हैं और तर्क को स्वयं लागू नहीं करते हैं, जैसा कि प्रारंभिक लिस्टिंग में दिखाया गया है, हमें इस बारे में जानकारी नहीं मिलती है कि क्या पूरा हुआ।
अन्य महत्वपूर्ण "असंगतता" तब होती है जब "एक से अधिक त्रुटि" होती है। इसलिए _id: 4
. के लिए अतिरिक्त मान को रद्द करना हमें देता है:
Has write Errors:
[
{
"code": 11000,
"index": 2,
"errmsg": "E11000 duplicate key error collection: test.songs index: _id_ dup key: { : 2 }",
"op": {
"__v": 0,
"_id": 2,
"name": "something else entirely"
}
},
{
"code": 11000,
"index": 5,
"errmsg": "E11000 duplicate key error collection: test.songs index: _id_ dup key: { : 4 }",
"op": {
"__v": 0,
"_id": 4,
"name": "different thing again"
}
}
]
यहां आप e.writeErrors
. की उपस्थिति में "शाखाओं में" कोड देख सकते हैं , जो मौजूद नहीं है जब एक . है त्रुटि। इसके विपरीत पहले के response
ऑब्जेक्ट में hasWriteErrors()
दोनों हैं और getWriteErrors()
विधियों, किसी भी त्रुटि के मौजूद होने की परवाह किए बिना। तो यह अधिक सुसंगत इंटरफ़ेस है और यही कारण है कि आपको err
का निरीक्षण करने के बजाय इसका उपयोग करना चाहिए अकेले प्रतिक्रिया।
MongoDB 3.x ड्राइवर फिक्स
यह व्यवहार वास्तव में ड्राइवर की आगामी 3.x रिलीज़ में तय किया गया है जो कि MongoDB 3.6 सर्वर रिलीज़ के साथ मेल खाने के लिए है। इसमें व्यवहार बदल जाता है err
प्रतिक्रिया मानक result
. के समान है , लेकिन निश्चित रूप से BulkWriteError
. के रूप में वर्गीकृत किया गया है MongoError
के बजाय प्रतिक्रिया जो वर्तमान में है।
जब तक इसे जारी नहीं किया जाता है (और निश्चित रूप से उस निर्भरता और परिवर्तनों को "मोंगोज़" कार्यान्वयन के लिए प्रचारित किया जाता है), तब कार्रवाई के अनुशंसित पाठ्यक्रम से अवगत होना चाहिए कि उपयोगी जानकारी result
में है और नहीं err
. वास्तव में आपके कोड को शायद hasErrors()
के लिए देखना चाहिए result
. में और फिर err
. की जांच करने के लिए फॉलबैक करें साथ ही, ड्राइवर में लागू किए जाने वाले परिवर्तन को पूरा करने के लिए।
लेखक नोट: इस सामग्री और संबंधित पठन में से अधिकांश वास्तव में पहले से ही यहां फंक्शन इंसर्टमैनी () अनियंत्रित:त्रुटियों और परिणाम दोनों को प्राप्त करने का उचित तरीका है? और MongoDB Node.js नेटिव ड्राइवर चुपचाप bulkWrite
. को निगल लेता है अपवाद। लेकिन यहां दोहराना और विस्तार करना जब तक कि यह अंततः लोगों में डूब नहीं जाता है कि इस तरह से आप वर्तमान ड्राइवर कार्यान्वयन में अपवादों को संभालते हैं। और यह वास्तव में तब काम करता है, जब आप सही जगह पर देखते हैं और उसी के अनुसार इसे संभालने के लिए अपना कोड लिखते हैं।