रेंज क्वेरी के साथ कीसेट पृष्ठांकन
शिक्षार्थी _id या टाइमस्टैम्प फ़ील्ड पर रेंज फ़िल्टर का उपयोग करके कर्सर-आधारित पृष्ठांकन बनाएँगे और हर पृष्ठ पर लगातार O(log n) प्रदर्शन प्राप्त करेंगे।
रेंज क्वेरी के साथ कीसेट पृष्ठांकन, CoddyKit पर MongoDB Academy का एक निःशुल्क पाठ है। यह 4 में से 3वाँ पाठ है। आप नीचे पूरा पाठ निःशुल्क पढ़ सकते हैं—फिर अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर के साथ ब्राउज़र में इसका व्यावहारिक अभ्यास कर सकते हैं। यह MongoDB Academy सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। MongoDB Academy पाठ्यक्रम में कुल 4 पाठ शामिल हैं।
कीसेट पृष्ठांकन क्या है?
कीसेट पृष्ठांकन—जिसे कर्सर पृष्ठांकन भी कहा जाता है—सॉर्ट कुंजी पर परास क्वेरी का उपयोग करके skip() से पूरी तरह बचता है। MongoDB को 'पहले N दस्तावेज़ों को पार करें' कहने के बजाय, आप कहते हैं 'ऐसे दस्तावेज़ दें जिनकी सॉर्ट कुंजी मेरे देखे गए अंतिम मान से बड़ी हो'। यह हमेशा O(log n) होता है, क्योंकि परिणाम-समूह में आप कितनी भी दूर हों, यह इंडेक्स परास स्कैन का उपयोग करता है।
मुख्य अवधारणा: कर्सर के रूप में परास फ़िल्टर
पहला पृष्ठ प्राप्त करने के बाद, लौटाए गए अंतिम दस्तावेज़ की सॉर्ट कुंजी का मान याद रखें। अगले पृष्ठ के लिए उन दस्तावेज़ों को फ़िल्टर करें जिनकी सॉर्ट कुंजी उस याद रखे गए मान से सख्ती से बड़ी हो (अवरोही क्रम में छोटी हो)। इंडेक्स के साथ यह फ़िल्टर MongoDB को सटीक प्रारंभिक बिंदु देता है—छोड़ने की आवश्यकता नहीं होती।
// First page — no cursor needed
const page1 = await db.collection('posts')
.find({ isPublished: true })
.sort({ createdAt: -1, _id: -1 })
.limit(20)
.toArray();
// Remember the last document's sort keys
const lastCreatedAt = page1[page1.length - 1].createdAt;
const lastId = page1[page1.length - 1]._id;परास क्वेरी से अगला पृष्ठ प्राप्त करना
अगले पृष्ठ की क्वेरी में याद रखे गए सॉर्ट कुंजी मानों का उपयोग $lt (या आरोही क्रम के लिए $gt) शर्त में करें। skip() की आवश्यकता नहीं है—परास शर्त इंडेक्स को सीधे सही प्रारंभिक स्थान पर ले जाती है। MongoDB उस बिंदु से शुरू करके limit दस्तावेज़ प्राप्त करता है।
// Next page: posts older than the last one seen
// Descending by createdAt means 'older' = $lt
const page2 = await db.collection('posts')
.find({
isPublished: true,
$or: [
{ createdAt: { $lt: lastCreatedAt } },
{ createdAt: lastCreatedAt, _id: { $lt: lastId } } // tiebreaker
]
})
.sort({ createdAt: -1, _id: -1 })
.limit(20)
.toArray();बराबरी-तोड़क के रूप में _id क्यों शामिल करें?
कई दस्तावेज़ों में समान createdAt टाइमस्टैम्प हो सकता है, जैसे एक ही सेकंड में डाले गए कई आइटम। बराबरी-तोड़क के बिना, परास सीमा अस्पष्ट होती है और सीमा पर दस्तावेज़ छूट या दोहराए जा सकते हैं। द्वितीयक सॉर्ट फ़ील्ड के रूप में _id जोड़ने और उसे परास शर्त में शामिल करने से कर्सर विशिष्ट रूप से नियत हो जाता है—किसी भी दस्तावेज़ में समान (createdAt, _id) युग्म नहीं हो सकता।
// Compound index to support the keyset query
db.posts.createIndex({ createdAt: -1, _id: -1 });
// This index covers both the sort and the range filterकेवल _id पर कीसेट पृष्ठांकन
यदि आप केवल _id के आधार पर सॉर्ट करते हैं (डिफ़ॉल्ट प्रविष्टि क्रम), तो कीसेट पृष्ठांकन का यह सबसे सरल रूप है। _id हमेशा अद्वितीय और पहले से इंडेक्स किया हुआ होता है। प्रत्येक पृष्ठ में ऐसे दस्तावेज़ लौटते हैं जिनका _id अंतिम देखे गए मान से बड़ा होता है। यह फ़ीड-जैसी क्वेरी के लिए पूरी तरह काम करता है, जहाँ प्रविष्टि क्रम स्वाभाविक सॉर्ट होता है।
// First page
const page1 = await db.collection('events')
.find({})
.sort({ _id: 1 })
.limit(50)
.toArray();
const lastId = page1[page1.length - 1]._id;
// Next page — range filter on _id
const page2 = await db.collection('events')
.find({ _id: { $gt: lastId } })
.sort({ _id: 1 })
.limit(50)
.toArray();API प्रतिक्रियाओं के लिए कर्सर को एन्कोड करना
API क्लाइंट को आंतरिक कर्सर प्रारूप जानने की आवश्यकता नहीं होनी चाहिए। कर्सर को ऐसे Base64 या JWT स्ट्रिंग के रूप में एन्कोड करें जिसे सर्वर अगले अनुरोध पर डिकोड कर सके। इससे क्लाइंट से कार्यान्वयन संबंधी विवरण (चाहे आप createdAt, _id या संयुक्त कुंजी का उपयोग करें) छिपा रहता है और API अनुबंध तोड़े बिना कर्सर प्रारूप बदलने की सुविधा मिलती है।
// Encode cursor
function encodeCursor(doc) {
return Buffer.from(JSON.stringify({ createdAt: doc.createdAt, _id: doc._id })).toString('base64');
}
// Decode cursor
function decodeCursor(token) {
return JSON.parse(Buffer.from(token, 'base64').toString('utf-8'));
}
// API response
const nextCursor = page.length === PAGE_SIZE ? encodeCursor(page[page.length - 1]) : null;
res.json({ data: page, nextCursor });Express हैंडलर में कीसेट पृष्ठांकन
एक पूर्ण कीसेट पृष्ठांकन हैंडलर आने वाले कर्सर को (यदि दिया गया हो) डिकोड करता है, परास फ़िल्टर बनाता है, क्वेरी चलाता है, अगला कर्सर एन्कोड करता है और प्रतिक्रिया लौटाता है। यदि लौटाने के लिए अगला कर्सर नहीं है (पृष्ठ का आकार पृष्ठ आकार से छोटा है), तो क्लाइंट समझ सकता है कि वह अंतिम पृष्ठ पर पहुँच गया है।
async function listPosts(req, res) {
const limit = 20;
let filter = { isPublished: true };
if (req.query.cursor) {
const { createdAt, _id } = decodeCursor(req.query.cursor);
filter['$or'] = [
{ createdAt: { $lt: new Date(createdAt) } },
{ createdAt: new Date(createdAt), _id: { $lt: _id } }
];
}
const posts = await Post.find(filter).sort({ createdAt: -1, _id: -1 }).limit(limit).lean();
const nextCursor = posts.length === limit ? encodeCursor(posts[posts.length - 1]) : null;
res.json({ data: posts, nextCursor });
}कीसेट बनाम ऑफ़सेट: प्रदर्शन तुलना
1,000,000 posts वाले संग्रह की कल्पना करें। पृष्ठ 1000 (प्रति पृष्ठ 20 आइटम) तक पहुँचने के लिए ऑफ़सेट पृष्ठांकन skip(19980) चलाता है—MongoDB 19,980 इंडेक्स प्रविष्टियाँ पार करता है। कीसेट पृष्ठांकन परास फ़िल्टर का उपयोग करता है: { createdAt: { $lt: someDate } }—MongoDB प्रारंभिक बिंदु खोजने के लिए इंडेक्स पर बाइनरी खोज करता है और ठीक 20 प्रविष्टियाँ स्कैन करता है। बड़े पैमाने पर अंतर मिलीसेकंड बनाम सेकंड का होता है।
कीसेट पृष्ठांकन की सीमाएँ
कीसेट पेजिनेशन की दो उल्लेखनीय सीमाएँ हैं: (1) आप किसी मनमाने पृष्ठ संख्या पर सीधे नहीं जा सकते—आप एक बार में केवल एक पृष्ठ आगे या पीछे जा सकते हैं; (2) सॉर्ट फ़ील्ड कर्सर का हिस्सा होना चाहिए, इसलिए गैर-अद्वितीय, गैर-अनुक्रमित फ़ील्ड के आधार पर सॉर्ट करते समय सही टाई-ब्रेकर चुनना आवश्यक है। इन समझौतों के कारण कीसेट पेजिनेशन उन अनुप्रयोगों के लिए उपयुक्त नहीं है जिनमें पृष्ठ-संख्या के आधार पर नेविगेशन आवश्यक हो, लेकिन अनंत स्क्रॉल और API कर्सर पैटर्न के लिए यह सही विकल्प है।
द्विदिशीय कीसेट पेजिनेशन
‘अगला पृष्ठ’ और ‘पिछला पृष्ठ’ दोनों प्रकार के नेविगेशन को समर्थित करने के लिए, प्रत्येक पृष्ठ पर पहले दस्तावेज़ और अंतिम दस्तावेज़—दोनों के कर्सर रखें। पीछे जाने के लिए पहले दस्तावेज़ के कर्सर के साथ $gt का उपयोग करें। पीछे की ओर की जाने वाली क्वेरी के लिए सॉर्ट दिशा उलट दें, फिर परिणाम लौटाने से पहले उन्हें दोबारा उलट दें।
// Previous page — documents newer than the first item on the current page
const prevPage = await db.collection('posts')
.find({
isPublished: true,
createdAt: { $gt: firstDocCreatedAt }
})
.sort({ createdAt: 1, _id: 1 }) // reverse sort for previous page
.limit(20)
.toArray();
prevPage.reverse(); // flip back to descending display orderकीसेट पेजिनेशन के लिए अनुक्रमणिका डिज़ाइन
कीसेट पेजिनेशन क्वेरी की संयुक्त अनुक्रमणिका में ये फ़ील्ड शामिल होने चाहिए: पहले फ़िल्टर फ़ील्ड (समानता की शर्तें), फिर सॉर्ट फ़ील्ड। उदाहरण के लिए, यदि आप isPublished से फ़िल्टर करते हैं और createdAt DESC, _id DESC के आधार पर सॉर्ट करते हैं, तो आदर्श अनुक्रमणिका { isPublished: 1, createdAt: -1, _id: -1 } है। यह अनुक्रमणिका बिना किसी मेमोरी-आधारित प्रक्रिया के समानता फ़िल्टर और सीमा-आधारित सॉर्ट—दोनों को संभालती है।
// Ideal covering index for keyset pagination on posts
db.posts.createIndex({ isPublished: 1, createdAt: -1, _id: -1 });
// Verify with explain — expect IXSCAN, no SORT stage
db.posts.find({ isPublished: true, createdAt: { $lt: new Date() } })
.sort({ createdAt: -1, _id: -1 })
.limit(20)
.explain('executionStats');त्वरित जाँच
इस पाठ में MongoDB और NoSQL डेटाबेस की अवधारणाओं के बारे में अपनी समझ जाँचें।
पाठ का पुनरावलोकन
इस पाठ में आपने सीखा: कीसेट पेजिनेशन skip() के बजाय अंतिम देखी गई सॉर्ट कुंजी पर सीमा-आधारित फ़िल्टर का उपयोग करता है, इससे पृष्ठ की गहराई चाहे जितनी हो, O(log n) प्रदर्शन प्राप्त होता है, और _id को टाई-ब्रेकर के रूप में शामिल करने से सॉर्ट की सीमाओं पर दस्तावेज़ों के दोहरने या छूटने की समस्या रुकती है। आगे हम sort, skip, limit और projections को मिलाकर एक पूरी क्वेरी शृंखला का अभ्यास करेंगे।
एआई शिक्षक के साथ JavaScript सीखें — निःशुल्क
अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।
- पाठ्यक्रम
- 30
- पाठ
- 120
अक्सर पूछे जाने वाले प्रश्न
क्या “रेंज क्वेरी के साथ कीसेट पृष्ठांकन” पाठ निःशुल्क है?
हाँ—“रेंज क्वेरी के साथ कीसेट पृष्ठांकन” का पूरा पाठ यहाँ वेब पर निःशुल्क पढ़ा जा सकता है। इंटरैक्टिव अभ्यास (अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर) करने और MongoDB Academy पाठ्यक्रम का बाकी हिस्सा अनलॉक करने के लिए CoddyKit PRO लें। MongoDB Academy पाठ्यक्रम में कुल 4 पाठ शामिल हैं।
“रेंज क्वेरी के साथ कीसेट पृष्ठांकन” में मैं क्या सीखूँगा?
शिक्षार्थी _id या टाइमस्टैम्प फ़ील्ड पर रेंज फ़िल्टर का उपयोग करके कर्सर-आधारित पृष्ठांकन बनाएँगे और हर पृष्ठ पर लगातार O(log n) प्रदर्शन प्राप्त करेंगे। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ MongoDB Academy का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।
क्या MongoDB Academy शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?
पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर MongoDB Academy शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 3वाँ पाठ है।
“रेंज क्वेरी के साथ कीसेट पृष्ठांकन” पाठ पूरा करने में कितना समय लगता है?
CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।
क्या मैं इस MongoDB Academy पाठ में कोड लिख और चला सकता हूँ?
हाँ। हर MongoDB Academy पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।
इस पाठ्यक्रम के सभी पाठ
- sort() और कई कुंजियों से क्रमबद्ध करना
- Skip और Limit: ऑफ़सेट पृष्ठांकन
- रेंज क्वेरी के साथ कीसेट पृष्ठांकन
- Sort, Skip, Limit और प्रोजेक्शन का संयोजन