जैसा कि पहले टिप्पणी में उल्लेख किया गया है, आपके पास यह पता लगाने के लिए दो बुनियादी दृष्टिकोण हैं कि कुछ "बनाया" गया था या नहीं। ये या तो हैं:
-
rawResult
लौटाएं प्रतिक्रिया में औरupdatedExisting
. की जांच करें संपत्ति जो आपको बताती है कि यह "अप्सर्ट" है या नहीं -
सेट करें
new: false
ताकि "कोई दस्तावेज़ नहीं" वास्तव में परिणाम में वापस आ जाए जब यह वास्तव में "अप्सर्ट" हो
प्रदर्शित करने के लिए एक सूची के रूप में:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/thereornot';
mongoose.set('debug', true);
mongoose.Promise = global.Promise;
const userSchema = new Schema({
username: { type: String, unique: true }, // Just to prove a point really
password: String
});
const User = mongoose.model('User', userSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
// Shows updatedExisting as false - Therefore "created"
let bill1 = await User.findOneAndUpdate(
{ username: 'Bill' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: true, rawResult: true }
);
log(bill1);
// Shows updatedExisting as true - Therefore "existing"
let bill2 = await User.findOneAndUpdate(
{ username: 'Bill' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: true, rawResult: true }
);
log(bill2);
// Test with something like:
// if ( bill2.lastErrorObject.updatedExisting ) throw new Error("already there");
// Return will be null on "created"
let ted1 = await User.findOneAndUpdate(
{ username: 'Ted' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: false }
);
log(ted1);
// Return will be an object where "existing" and found
let ted2 = await User.findOneAndUpdate(
{ username: 'Ted' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: false }
);
log(ted2);
// Test with something like:
// if (ted2 !== null) throw new Error("already there");
// Demonstrating "why" we reserve the "Duplicate" error
let fred1 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'password' },
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
log(fred1); // null - so okay
let fred2 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'badpassword' }, // <-- dup key for wrong password
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
mongoose.disconnect();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
और आउटपुट:
Mongoose: users.remove({}, {})
Mongoose: users.findAndModify({ username: 'Bill' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: true, rawResult: true, remove: false, fields: {} })
{
"lastErrorObject": {
"n": 1,
"updatedExisting": false,
"upserted": "5adfc8696878cfc4992e7634"
},
"value": {
"_id": "5adfc8696878cfc4992e7634",
"username": "Bill",
"__v": 0,
"password": "password"
},
"ok": 1,
"operationTime": "6548172736517111811",
"$clusterTime": {
"clusterTime": "6548172736517111811",
"signature": {
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId": 0
}
}
}
Mongoose: users.findAndModify({ username: 'Bill' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: true, rawResult: true, remove: false, fields: {} })
{
"lastErrorObject": {
"n": 1,
"updatedExisting": true
},
"value": {
"_id": "5adfc8696878cfc4992e7634",
"username": "Bill",
"__v": 0,
"password": "password"
},
"ok": 1,
"operationTime": "6548172736517111811",
"$clusterTime": {
"clusterTime": "6548172736517111811",
"signature": {
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId": 0
}
}
}
Mongoose: users.findAndModify({ username: 'Ted' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: false, remove: false, fields: {} })
null
Mongoose: users.findAndModify({ username: 'Ted' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: false, remove: false, fields: {} })
{
"_id": "5adfc8696878cfc4992e7639",
"username": "Ted",
"__v": 0,
"password": "password"
}
तो पहला मामला वास्तव में इस कोड पर विचार करता है:
User.findOneAndUpdate(
{ username: 'Bill' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: true, rawResult: true }
)
अधिकांश विकल्प यहां "सभी" . के रूप में मानक हैं "upsert"
कार्रवाइयों के परिणामस्वरूप फ़ील्ड सामग्री का उपयोग "मिलान" करने के लिए किया जाएगा (अर्थात username
) "हमेशा" . है नए दस्तावेज़ में बनाया गया है, इसलिए आपको $set
वह क्षेत्र। बाद के अनुरोधों पर अन्य फ़ील्ड को वास्तव में "संशोधित" न करने के लिए आप का उपयोग कर सकते हैं। $setOnInsert
, जो इन गुणों को केवल "upsert"
. के दौरान जोड़ता है कार्रवाई जहां कोई मिलान नहीं मिला।
यहां मानक new: true
कार्रवाई से "संशोधित" दस्तावेज़ वापस करने के लिए प्रयोग किया जाता है, लेकिन अंतर rawResult
में है जैसा कि लौटाई गई प्रतिक्रिया में दिखाया गया है:
{
"lastErrorObject": {
"n": 1,
"updatedExisting": false,
"upserted": "5adfc8696878cfc4992e7634"
},
"value": {
"_id": "5adfc8696878cfc4992e7634",
"username": "Bill",
"__v": 0,
"password": "password"
},
"ok": 1,
"operationTime": "6548172736517111811",
"$clusterTime": {
"clusterTime": "6548172736517111811",
"signature": {
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId": 0
}
}
}
"नेवला दस्तावेज़" के बजाय आपको ड्राइवर से वास्तविक "कच्ची" प्रतिक्रिया मिलती है। वास्तविक दस्तावेज़ सामग्री "value"
. के अंतर्गत है संपत्ति है, लेकिन यह "lastErrorObject"
है हम इसमें रुचि रखते हैं।
यहां हम संपत्ति देखते हैं updatedExisting: false
. यह इंगित करता है कि "कोई मिलान नहीं" वास्तव में पाया गया था, इस प्रकार एक नया दस्तावेज़ "बनाया गया" था। तो आप इसका उपयोग यह निर्धारित करने के लिए कर सकते हैं कि सृजन वास्तव में हुआ था।
जब आप वही क्वेरी विकल्प दोबारा जारी करते हैं, तो परिणाम अलग होगा:
{
"lastErrorObject": {
"n": 1,
"updatedExisting": true // <--- Now I'm true
},
"value": {
"_id": "5adfc8696878cfc4992e7634",
"username": "Bill",
"__v": 0,
"password": "password"
},
"ok": 1,
"operationTime": "6548172736517111811",
"$clusterTime": {
"clusterTime": "6548172736517111811",
"signature": {
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"keyId": 0
}
}
}
updatedExisting
मान अब true
है , और ऐसा इसलिए है क्योंकि पहले से ही एक दस्तावेज़ था जो username: 'Bill'
से मेल खाता था क्वेरी स्टेटमेंट में। यह आपको बताता है कि दस्तावेज़ पहले से ही था, इसलिए आप अपने तर्क को "त्रुटि" या जो भी प्रतिक्रिया चाहते हैं उसे वापस करने के लिए शाखा कर सकते हैं।
दूसरे मामले में, यह वांछनीय हो सकता है कि "कच्ची" प्रतिक्रिया को "नहीं" लौटाया जाए और इसके बजाय एक लौटा हुआ "नेवला दस्तावेज़" का उपयोग किया जाए। इस मामले में हम मान बदलते हैं new: false
बिना rawResult
. के विकल्प।
User.findOneAndUpdate(
{ username: 'Ted' },
{ $setOnInsert: { password: 'password' } },
{ upsert: true, new: false }
)
अधिकांश समान चीजें लागू होती हैं सिवाय इसके कि अब कार्रवाई मूल है कार्रवाई के बाद दस्तावेज़ की "संशोधित" स्थिति के विपरीत दस्तावेज़ की स्थिति लौटा दी जाती है। इसलिए जब कोई दस्तावेज़ नहीं है जो वास्तव में "क्वेरी" कथन से मेल खाता है, तो दिया गया परिणाम null
है :
Mongoose: users.findAndModify({ username: 'Ted' }, [], { '$setOnInsert': { password: 'password', __v: 0 } }, { upsert: true, new: false, remove: false, fields: {} })
null // <-- Got null in response :(
यह आपको बताता है कि दस्तावेज़ "बनाया गया" था, और यह तर्कपूर्ण है कि आप पहले से ही जानते हैं कि दस्तावेज़ की सामग्री क्या होनी चाहिए क्योंकि आपने उस डेटा को कथन के साथ भेजा था (आदर्श रूप से $setOnInsert
में) ) बिंदु होने के नाते, आप पहले से ही जानते हैं कि दस्तावेज़ सामग्री को वास्तव में वापस करने के लिए आपको "चाहिए" क्या लौटाना है।
इसके विपरीत, एक "पाया गया" दस्तावेज़ "मूल स्थिति" लौटाता है जो दस्तावेज़ को "इससे पहले" संशोधित करता है:
{
"_id": "5adfc8696878cfc4992e7639",
"username": "Ted",
"__v": 0,
"password": "password"
}
इसलिए कोई भी प्रतिक्रिया जो "नहीं null
. है " इसलिए एक संकेत है कि दस्तावेज़ पहले से ही मौजूद था, और फिर से आप अपने तर्क को इस आधार पर विभाजित कर सकते हैं कि वास्तव में प्रतिक्रिया में क्या प्राप्त हुआ था।
तो वे दो बुनियादी दृष्टिकोण हैं जो आप पूछ रहे हैं, और वे निश्चित रूप से "काम करते हैं"! और जैसा कि यहाँ उन्हीं कथनों के साथ प्रदर्शित और प्रतिलिपि प्रस्तुत करने योग्य है।
परिशिष्ट - खराब पासवर्ड के लिए डुप्लीकेट कुंजी सुरक्षित रखें
एक और वैध दृष्टिकोण है जो पूरी सूची में भी संकेत दिया गया है, जो अनिवार्य रूप से केवल .insert()
है। (या .create()
नेवला मॉडल से) नया डेटा और एक "डुप्लिकेट कुंजी" त्रुटि फेंक है जहां सूचकांक द्वारा "अद्वितीय" संपत्ति वास्तव में सामने आई है। यह एक वैध दृष्टिकोण है लेकिन "उपयोगकर्ता सत्यापन" में एक विशेष उपयोग मामला है जो तर्क प्रबंधन का एक आसान टुकड़ा है, और वह है "पासवर्ड मान्य करना"।
तो यह username
. द्वारा उपयोगकर्ता जानकारी पुनर्प्राप्त करने के लिए एक बहुत ही सामान्य पैटर्न है और password
संयोजन। "अप्सर्ट" के मामले में यह संयोजन "अद्वितीय" के रूप में उचित ठहराता है और इसलिए कोई मिलान नहीं मिलने पर "इन्सर्ट" का प्रयास किया जाता है। यह वही है जो पासवर्ड के मिलान को यहां एक उपयोगी कार्यान्वयन बनाता है।
निम्नलिखित पर विचार करें:
// Demonstrating "why" we reserve the "Duplicate" error
let fred1 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'password' },
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
log(fred1); // null - so okay
let fred2 = await User.findOneAndUpdate(
{ username: 'Fred', password: 'badpassword' }, // <-- dup key for wrong password
{ $setOnInsert: { } },
{ upsert: true, new: false }
);
पहले प्रयास में हमारे पास वास्तव में username
नहीं होता है "Fred"
. के लिए , इसलिए "अप्सर्ट" होगा और अन्य सभी चीजें, जैसा कि ऊपर वर्णित है, यह पहचानने के लिए होती है कि यह एक रचना थी या एक पाया गया दस्तावेज़।
निम्नलिखित कथन समान username
. का उपयोग करता है मूल्य लेकिन जो रिकॉर्ड किया गया है उसके लिए एक अलग पासवर्ड प्रदान करता है। यहाँ MongoDB नए दस्तावेज़ को "बनाने" का प्रयास करता है क्योंकि यह संयोजन पर मेल नहीं खाता, बल्कि इसलिए कि username
"unique"
होने की उम्मीद है आपको एक "डुप्लिकेट कुंजी त्रुटि" प्राप्त होती है:
{ MongoError: E11000 duplicate key error collection: thereornot.users index: username_1 dup key: { : "Fred" }
तो आपको क्या एहसास होना चाहिए कि अब आपको तीन . मिलते हैं "मुक्त" के लिए मूल्यांकन करने की शर्तें। होना:
- "अप्सर्ट" को
updatedExisting: false
. द्वारा रिकॉर्ड किया गया था याnull
विधि के आधार पर परिणाम। - आप जानते हैं कि दस्तावेज़ (संयोजन द्वारा) या तो
updatedExisting: true
के माध्यम से "मौजूद" है या जहां दस्तावेज़ रिटर्न "नहींnull
. था ". - यदि
password
बशर्तेusername
. के लिए पहले से मौजूद चीज़ों के लिए एक मेल नहीं था , तो आपको "डुप्लिकेट कुंजी त्रुटि" प्राप्त होगी जिसे आप ट्रैप कर सकते हैं और तदनुसार प्रतिक्रिया दे सकते हैं, उपयोगकर्ता को "पासवर्ड गलत है" के जवाब में सलाह दे सकते हैं।
वह सब एक . से अनुरोध।
संग्रह में केवल सम्मिलन फेंकने के विरोध में "अप्सर्ट्स" का उपयोग करने का यही मुख्य तर्क है, क्योंकि आप डेटाबेस को अतिरिक्त अनुरोध किए बिना तर्क की अलग-अलग शाखाएं प्राप्त कर सकते हैं ताकि यह निर्धारित किया जा सके कि उन स्थितियों में से "कौन सी" वास्तविक प्रतिक्रिया होनी चाहिए।पी>