दुर्भाग्य से mgo.v2
ड्राइवर cursor.min()
specify निर्दिष्ट करने के लिए API कॉल प्रदान नहीं करता है ।
लेकिन एक हल है। mgo.Database
प्रकार एक Database.Run()
प्रदान करता है किसी भी MongoDB कमांड को चलाने की विधि। उपलब्ध कमांड और उनके दस्तावेज यहां देखे जा सकते हैं:डेटाबेस कमांड्स
MongoDB 3.2 से शुरू होकर, एक नया find
कमांड उपलब्ध है जिसका उपयोग प्रश्नों को निष्पादित करने के लिए किया जा सकता है, और यह निर्दिष्ट करने का समर्थन करता है min
तर्क जो सूची परिणामों को शुरू करने वाली पहली अनुक्रमणिका प्रविष्टि को दर्शाता है।
अच्छा। प्रत्येक बैच (पृष्ठ के दस्तावेज़) के बाद min
. उत्पन्न करने के बाद हमें क्या करना है क्वेरी परिणाम के अंतिम दस्तावेज़ से दस्तावेज़, जिसमें अनुक्रमणिका प्रविष्टि के मान शामिल होने चाहिए जिसका उपयोग क्वेरी को निष्पादित करने के लिए किया गया था, और फिर अगला बैच (अगले पृष्ठ के दस्तावेज़) इस न्यूनतम अनुक्रमणिका प्रविष्टि को पहले सेट करके प्राप्त किया जा सकता है क्वेरी निष्पादित करने के लिए।
यह अनुक्रमणिका प्रविष्टि - चलो इसे कर्सर कहते हैं अब से– को string
. में एन्कोड किया जा सकता है और क्लाइंट को परिणामों के साथ भेजा जाता है, और जब क्लाइंट अगला पेज चाहता है, तो वह कर्सर को वापस भेजता है कह रहा है कि वह इस कर्सर के बाद परिणाम शुरू करना चाहता है।
इसे मैन्युअल रूप से करना ("कठिन" तरीका)
निष्पादित की जाने वाली कमांड विभिन्न रूपों में हो सकती है, लेकिन कमांड का नाम (find
) मार्शल किए गए परिणाम में पहले होना चाहिए, इसलिए हम bson.D
. का उपयोग करेंगे (जो bson.M
. के विपरीत ऑर्डर को सुरक्षित रखता है ):
limit := 10
cmd := bson.D{
{Name: "find", Value: "users"},
{Name: "filter", Value: bson.M{"country": "USA"}},
{Name: "sort", Value: []bson.D{
{Name: "name", Value: 1},
{Name: "_id", Value: 1},
},
{Name: "limit", Value: limit},
{Name: "batchSize", Value: limit},
{Name: "singleBatch", Value: true},
}
if min != nil {
// min is inclusive, must skip first (which is the previous last)
cmd = append(cmd,
bson.DocElem{Name: "skip", Value: 1},
bson.DocElem{Name: "min", Value: min},
)
}
MongoDB निष्पादित करने का परिणाम find
Database.Run()
. के साथ कमांड निम्न प्रकार से कैप्चर किया जा सकता है:
var res struct {
OK int `bson:"ok"`
WaitedMS int `bson:"waitedMS"`
Cursor struct {
ID interface{} `bson:"id"`
NS string `bson:"ns"`
FirstBatch []bson.Raw `bson:"firstBatch"`
} `bson:"cursor"`
}
db := session.DB("")
if err := db.Run(cmd, &res); err != nil {
// Handle error (abort)
}
अब हमारे पास परिणाम हैं, लेकिन एक प्रकार के []bson.Raw
. में . लेकिन हम इसे []*User
. प्रकार के एक स्लाइस में चाहते हैं . यह वह जगह है जहां Collection.NewIter()
काम आता है। यह []bson.Raw
. प्रकार के मान को (unmarshal) रूपांतरित कर सकता है किसी भी प्रकार में हम आमतौर पर Query.All()
. पास करते हैं या Iter.All()
. अच्छा। आइए इसे देखें:
firstBatch := res.Cursor.FirstBatch
var users []*User
err = db.C("users").NewIter(nil, firstBatch, 0, nil).All(&users)
अब हमारे पास अगले पृष्ठ के उपयोगकर्ता हैं। केवल एक ही चीज़ बची है:यदि हमें कभी इसकी आवश्यकता हो तो अगला पृष्ठ प्राप्त करने के लिए उपयोग किए जाने वाले कर्सर को उत्पन्न करना:
if len(users) > 0 {
lastUser := users[len(users)-1]
cursorData := []bson.D{
{Name: "country", Value: lastUser.Country},
{Name: "name", Value: lastUser.Name},
{Name: "_id", Value: lastUser.ID},
}
} else {
// No more users found, use the last cursor
}
यह सब ठीक है, लेकिन हम cursorData
को कैसे रूपांतरित करते हैं? करने के लिए string
और इसके विपरीत? हम bson.Marshal()
. का उपयोग कर सकते हैं और bson.Unmarshal()
बेस 64 एन्कोडिंग के साथ संयुक्त; base64.RawURLEncoding
. का उपयोग हमें एक वेब-सुरक्षित कर्सर स्ट्रिंग देगा, जिसे बिना बच निकले URL क्वेरी में जोड़ा जा सकता है।
यहां एक उदाहरण कार्यान्वयन है:
// CreateCursor returns a web-safe cursor string from the specified fields.
// The returned cursor string is safe to include in URL queries without escaping.
func CreateCursor(cursorData bson.D) (string, error) {
// bson.Marshal() never returns error, so I skip a check and early return
// (but I do return the error if it would ever happen)
data, err := bson.Marshal(cursorData)
return base64.RawURLEncoding.EncodeToString(data), err
}
// ParseCursor parses the cursor string and returns the cursor data.
func ParseCursor(c string) (cursorData bson.D, err error) {
var data []byte
if data, err = base64.RawURLEncoding.DecodeString(c); err != nil {
return
}
err = bson.Unmarshal(data, &cursorData)
return
}
और अंत में हमारे पास हमारा कुशल, लेकिन इतना छोटा MongoDB नहीं है mgo
पेजिंग कार्यक्षमता। आगे पढ़ें...
github.com/icza/minquery
का उपयोग करना ("आसान" तरीका)
मैनुअल तरीका काफी लंबा है; इसे सामान्य बनाया जा सकता है और स्वचालित . यह वह जगह है जहां github.com/icza/minquery
चित्र में आता है (प्रकटीकरण:मैं लेखक हूं ) यह MongoDB को कॉन्फ़िगर करने और निष्पादित करने के लिए एक आवरण प्रदान करता है find
कमांड, आपको एक कर्सर निर्दिष्ट करने की अनुमति देता है, और क्वेरी निष्पादित करने के बाद, यह आपको परिणामों के अगले बैच को क्वेरी करने के लिए उपयोग किए जाने वाले नए कर्सर को वापस देता है। रैपर MinQuery
है टाइप करें जो mgo.Query
. से बहुत मिलता-जुलता है लेकिन यह MongoDB के min
. निर्दिष्ट करने का समर्थन करता है MinQuery.Cursor()
. के माध्यम से विधि।
उपरोक्त समाधान minquery
. का उपयोग कर रहे हैं ऐसा दिखता है:
q := minquery.New(session.DB(""), "users", bson.M{"country" : "USA"}).
Sort("name", "_id").Limit(10)
// If this is not the first page, set cursor:
// getLastCursor() represents your logic how you acquire the last cursor.
if cursor := getLastCursor(); cursor != "" {
q = q.Cursor(cursor)
}
var users []*User
newCursor, err := q.All(&users, "country", "name", "_id")
और यह सबकुछ है। newCursor
अगले बैच को लाने के लिए उपयोग किया जाने वाला कर्सर है।
नोट #1: कॉल करते समय MinQuery.All()
, आपको कर्सर फ़ील्ड के नाम प्रदान करने होंगे, इसका उपयोग कर्सर डेटा (और अंततः कर्सर स्ट्रिंग) को बनाने के लिए किया जाएगा।
नोट #2: यदि आप आंशिक परिणाम प्राप्त कर रहे हैं (MinQuery.Select()
. का उपयोग करके ), आपको उन सभी क्षेत्रों को शामिल करना होगा जो कर्सर (सूचकांक प्रविष्टि) का हिस्सा हैं, भले ही आप उन्हें सीधे उपयोग करने का इरादा नहीं रखते हैं, अन्यथा MinQuery.All()
कर्सर फ़ील्ड के सभी मान नहीं होंगे, और इसलिए यह उचित कर्सर मान बनाने में सक्षम नहीं होगा।
minquery
. का पैकेज दस्तावेज़ देखें यहां:https://godoc.org/github.com/icza/minquery, यह अपेक्षाकृत छोटा है और उम्मीद के मुताबिक साफ है।