sql >> データベース >  >> NoSQL >> MongoDB

mgoを使用したMongoDBでの効率的なページング

    残念ながら、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、かなり短く、うまくいけばきれいです。




    1. 次のストップ–エッジからインサイトへのデータパイプラインの構築

    2. phpでアップサートした後にmongodb_idオブジェクトを取得します

    3. MongoDBバージョンを確認する7つの方法

    4. TrelloでRedisはどのように使用されますか?