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

Mgoアグリゲーション:モデルタイプを再利用して、混合結果をクエリおよびアンマーシャリングする方法は?

    上記のクエリは、Userに「ほぼ」一致するドキュメントを返します ドキュメントですが、各ユーザーの投稿もあります。つまり、基本的に結果は一連のUser Postのあるドキュメント 配列またはスライス埋め込み

    1つの方法は、Posts []*Postを追加することです。 Userへのフィールド それ自体、そして私たちは完了します:

    type User struct {
        ID         string    `bson:"_id"`
        Name       string    `bson:"name"`
        Registered time.Time `bson:"registered"`
        Posts      []*Post   `bson:"posts,omitempty"`
    }
    

    これは機能しますが、Userを拡張するのは「やり過ぎ」のようです。 Postsを使用 単一のクエリのためだけに。この道を進むとしたら、User タイプは、さまざまなクエリに対して多くの「余分な」フィールドで肥大化するでしょう。 Postsに入力するかどうかは言うまでもありません フィールドにユーザーを保存すると、それらの投稿はUser内に保存されます。 資料。私たちが望んでいるものではありません。

    もう1つの方法は、UserWithPostsを作成することです。 タイプコピーUser 、およびPosts []*Postを追加します 分野。言うまでもなく、これは醜くて柔軟性がありません(Userに加えられた変更) UserWithPostsに反映する必要があります 手動で)。

    構造体埋め込みあり

    元のUserを変更する代わりに 、新しいUserWithPostsを作成する代わりに 「scratch」から入力すると、構造体埋め込み を利用できます。 (既存のUserを再利用します およびPost タイプ)ちょっとしたトリックで:

    type UserWithPosts struct {
        User  `bson:",inline"`
        Posts []*Post `bson:"posts"`
    }
    

    bson タグの値に注意してください ",inline" 。これは、 bson.Marshal()に記載されています。 および bson.Unmarshal() (マーシャリング解除に使用します):

    埋め込みと",inline"を使用する タグ値、UserWithPosts タイプ自体は、Userのマーシャリングを解除するための有効なターゲットになります ドキュメントとそのPost []*Post フィールドは、検索された"posts"に最適です。 。

    使用:

    var uwp *UserWithPosts
    it := pipe.Iter()
    for it.Next(&uwp) {
        // Use uwp:
        fmt.Println(uwp)
    }
    // Handle it.Err()
    

    または、すべての結果を1つのステップで取得します:

    var uwps []*UserWithPosts
    err := pipe.All(&uwps)
    // Handle error
    

    UserWithPostsの型宣言 ローカル宣言である場合とそうでない場合があります。他の場所で必要ない場合は、集計クエリを実行および処理する関数のローカル宣言にすることができるため、既存の型と宣言が肥大化することはありません。再利用したい場合は、パッケージレベル(エクスポートまたは非エクスポート)で宣言し、必要な場所で使用できます。

    集計の変更

    もう1つのオプションは、MongoDBの $replaceRootを使用することです。 結果ドキュメントを「再配置」するため、「単純な」構造体がドキュメントを完全にカバーします。

    // Query users with their posts:
    pipe := collUsers.Pipe([]bson.M{
        {
            "$lookup": bson.M{
                "from":         "posts",
                "localField":   "_id",
                "foreignField": "userID",
                "as":           "posts",
            },
        },
        {
            "$replaceRoot": bson.M{
                "newRoot": bson.M{
                    "user":  "$$ROOT",
                    "posts": "$posts",
                },
            },
        },
    })
    

    この再マッピングにより、結果ドキュメントは次のようにモデル化できます。

    type UserWithPosts struct {
        User  *User   `bson:"user"`
        Posts []*Post `bson:"posts"`
    }
    

    これは機能しますが、postsであることに注意してください すべてのドキュメントのフィールドがサーバーから2回フェッチされます。1回はpostsとして取得されます。 返されたドキュメントのフィールド、およびuserのフィールドとして1回;マップ/使用はしませんが、結果ドキュメントに存在します。したがって、このソリューションを選択した場合、user.posts フィールドを削除する必要があります。例: $projectを使用 ステージ:

    pipe := collUsers.Pipe([]bson.M{
        {
            "$lookup": bson.M{
                "from":         "posts",
                "localField":   "_id",
                "foreignField": "userID",
                "as":           "posts",
            },
        },
        {
            "$replaceRoot": bson.M{
                "newRoot": bson.M{
                    "user":  "$$ROOT",
                    "posts": "$posts",
                },
            },
        },
        {"$project": bson.M{"user.posts": 0}},
    })
    



    1. mongodbで、$ in演算子と一致する配列要素のインデックスを知っていますか?

    2. 条件に一致する配列要素をカウントします

    3. マングース埋め込みドキュメントの更新

    4. MongoDB3.0WiredTigerでのインデックスプレフィックス圧縮