残念ながら、mgo.v2
ドライバは、cursor.min()
を指定するための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
の値を変換(アンマーシャリング)できます 通常Query.All()
に渡す任意のタイプに またはIter.All()
。良い。見てみましょう:
firstBatch := res.Cursor.FirstBatch
var users []*User
err = db.C("users").NewIter(nil, firstBatch, 0, nil).All(&users)
これで、次のページのユーザーができました。残っているのは1つだけです。必要になったときに次のページを取得するために使用するカーソルを生成します:
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()
base64エンコーディングと組み合わせる。 base64.RawURLEncoding
の使用 Webセーフなカーソル文字列が表示されます。これは、エスケープせずに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、かなり短く、うまくいけばきれいです。