Bohužel mgo.v2
ovladač neposkytuje volání API pro specifikaci cursor.min()
.
Ale existuje řešení. mgo.Database
type poskytuje Database.Run()
metoda pro spouštění jakýchkoli příkazů MongoDB. Dostupné příkazy a jejich dokumentaci naleznete zde:Databázové příkazy
Počínaje MongoDB 3.2, nový find
je k dispozici příkaz, který lze použít ke spouštění dotazů, a podporuje zadání min
argument, který označuje první položku rejstříku, od které se mají začít vypisovat výsledky.
Dobrý. Co musíme udělat, je po každé dávce (dokumentů na stránce) vygenerovat min
dokument z posledního dokumentu výsledku dotazu, který musí obsahovat hodnoty položky rejstříku, která byla použita k provedení dotazu, a poté lze získat další dávku (dokumenty na další stránce) nastavením této minimální položky rejstříku před k provedení dotazu.
Tato položka rejstříku – nazvěme ji kurzor od nynějška – může být zakódováno do string
a odeslána klientovi spolu s výsledky, a když klient chce další stránku, pošle zpět kurzor říká, že chce výsledky začínající po tomto kurzoru.
Proveďte to ručně ("tvrdým" způsobem)
Příkaz, který se má provést, může mít různé formy, ale název příkazu (find
) musí být v seřazeném výsledku první, takže použijeme bson.D
(který zachovává pořadí na rozdíl od 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},
)
}
Výsledek spuštění MongoDB find
pomocí Database.Run()
lze zachytit pomocí následujícího typu:
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)
}
Nyní máme výsledky, ale v řezu typu []bson.Raw
. Ale my to chceme v řezu typu []*User
. Zde je Collection.NewIter()
přijde vhod. Dokáže transformovat (odhlásit) hodnotu typu []bson.Raw
do jakéhokoli typu, který obvykle předáme do Query.All()
nebo Iter.All()
. Dobrý. Podívejme se na to:
firstBatch := res.Cursor.FirstBatch
var users []*User
err = db.C("users").NewIter(nil, firstBatch, 0, nil).All(&users)
Nyní máme uživatele další stránky. Zbývá pouze jedna věc:vygenerování kurzoru, který se použije k získání následující stránky, pokud bychom to někdy potřebovali:
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
}
To je všechno dobré, ale jak převedeme cursorData
na string
a naopak? Můžeme použít bson.Marshal()
a bson.Unmarshal()
v kombinaci s kódováním base64; použití base64.RawURLEncoding
nám poskytne webový kurzorový řetězec, který lze přidat do URL dotazů bez escapování.
Zde je příklad implementace:
// 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
}
A konečně máme náš efektivní, ale ne tak krátký MongoDB mgo
stránkovací funkce. Čtěte dále...
Pomocí github.com/icza/minquery
("snadný" způsob)
Ruční způsob je poměrně zdlouhavý; může být obecně a automatizované . Zde je github.com/icza/minquery
vstoupí do obrázku (zveřejnění:Jsem autor ). Poskytuje obal pro konfiguraci a spuštění MongoDB find
příkaz, který vám umožní zadat kurzor a po provedení dotazu vám vrátí nový kurzor, který se má použít k dotazu na další dávku výsledků. Obálka je MinQuery
typ, který je velmi podobný mgo.Query
ale podporuje specifikaci min
MongoDB prostřednictvím MinQuery.Cursor()
metoda.
Výše uvedené řešení pomocí minquery
vypadá takto:
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")
A to je vše. newCursor
je kurzor, který se má použít k načtení další dávky.
Poznámka č. 1: Při volání MinQuery.All()
, musíte zadat názvy polí kurzoru, z nichž se vytvoří data kurzoru (a nakonec řetězec kurzoru).
Poznámka č. 2: Pokud načítáte částečné výsledky (pomocí MinQuery.Select()
), musíte zahrnout všechna pole, která jsou součástí kurzoru (položka rejstříku), i když je přímo nechcete použít, jinak MinQuery.All()
nebude mít všechny hodnoty polí kurzoru, a tak nebude schopen vytvořit správnou hodnotu kurzoru.
Podívejte se na dokument balíčku minquery
zde:https://godoc.org/github.com/icza/minquery, je poměrně krátký a doufám, že čistý.