sql >> Databasteknik >  >> NoSQL >> MongoDB

Mgo-aggregering:hur återanvänder man modelltyper för att fråga och ta fram blandade resultat?

Frågan ovan returnerar dokument som "nästan" matchar User dokument, men de har också inlägg från varje användare. Så i grund och botten är resultatet en serie User dokument med en Post array eller segment inbäddad .

Ett sätt skulle vara att lägga till en Posts []*Post fältet till User själv, och vi skulle vara klara:

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

Även om detta fungerar verkar det "överkill" att utöka User med Posts bara för en enda frågas skull. Om vi ​​skulle fortsätta på den här vägen, vår User typ skulle bli uppsvälld med massor av "extra" fält för olika frågor. För att inte tala om om vi fyller Posts fältet och spara användaren, skulle dessa inlägg hamna sparade i User dokumentera. Inte vad vi vill ha.

Ett annat sätt skulle vara att skapa en UserWithPosts typ kopiering User , och lägga till en Posts []*Post fält. Onödigt att säga att detta är fult och oflexibelt (alla ändringar som gjorts i User skulle behöva återspeglas i UserWithPosts manuellt).

Med Struct Embedding

Istället för att ändra den ursprungliga User , och istället för att skapa en ny UserWithPosts skriv från "scratch", kan vi använda struct inbäddning (återanvändning av befintlig User och Post typer) med ett litet trick:

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

Notera bson taggvärdet ",inline" . Detta finns dokumenterat på bson.Marshal() och bson.Unmarshal() (vi kommer att använda det för att urskilja):

Genom att använda inbäddning och ",inline" taggvärdet, UserWithPosts typen i sig kommer att vara ett giltigt mål för att dela upp User dokument och dess Post []*Post fältet kommer att vara ett perfekt val för de uppslagna "posts" .

Använder det:

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

Eller få alla resultat i ett steg:

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

Typdeklarationen för UserWithPosts kan eller kanske inte är en lokal deklaration. Om du inte behöver det någon annanstans kan det vara en lokal deklaration i funktionen där du kör och bearbetar aggregeringsfrågan, så det kommer inte att blåsa upp dina befintliga typer och deklarationer. Om du vill återanvända den kan du deklarera den på paketnivå (exporterad eller ej exporterad) och använda den var du än behöver den.

Ändra aggregeringen

Ett annat alternativ är att använda MongoDB:s $replaceRoot för att "ordna om" resultatdokumenten, så en "enkel" struktur täcker dokumenten perfekt:

// 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",
            },
        },
    },
})

Med denna ommappning kan resultatdokumenten modelleras så här:

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

Observera att medan detta fungerar, gör posts fältet för alla dokument kommer att hämtas från servern två gånger:en gång som posts fältet för de returnerade dokumenten, och en gång som fältet för user; vi kartlägger / använder det inte men det finns i resultatdokumenten. Så om den här lösningen väljs visas user.posts fält bör tas bort t.ex. med ett $project steg:

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. Konfigurera GridFS Chunksize i MongoDB

  2. Hur använder man tredje parts bibliotek i glasfisk?

  3. MongoDB reguljärt uttryck med indexerat fält

  4. Hur man ökar befintligt värde i MongoDB