MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Mgo агрегиране:как да използвате повторно типове модели за заявки и демаршалиране на смесени резултати?

Горната заявка връща документи, които "почти" съответстват на User документи, но имат и публикациите на всеки потребител. Така че основно резултатът е поредица от User документи с Post масив или срез вграден .

Един от начините е да добавите 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 документ. Не е това, което искаме.

Друг начин би бил да създадете UserWithPosts тип копиране User и добавяне на Posts []*Post поле. Излишно е да казвам, че това е грозно и негъвкаво (всички промени, направени в User трябва да се отрази на UserWithPosts ръчно).

С вграждане на структура

Вместо да модифицирате оригиналния User и вместо да създадете нов UserWithPosts тип от "нулата", можем да използваме вграждане на структура (повторно използване на съществуващия 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()

Или получаване на всички резултати в една стъпка:

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

Декларацията на типа на UserWithPosts може или не може да бъде местна декларация. Ако не се нуждаете от него другаде, това може да бъде локална декларация във функцията, където изпълнявате и обработвате заявката за агрегиране, така че няма да раздуе вашите съществуващи типове и декларации. Ако искате да го използвате повторно, можете да го декларирате на ниво пакет (експортиран или неекспортиран) и да го използвате, където имате нужда.

Промяна на агрегацията

Друг вариант е да използвате $replaceRoot на MongoDB за да „пренаредите“ резултатите от документите, така че една „проста“ структура ще покрие перфектно документите:

// 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 полето на всички документи ще бъде извлечено от сървъра два пъти:веднъж като posts поле на върнатите документи и веднъж като поле на user; ние не го картографираме/използваме, но той присъства в документите с резултатите. Така че, ако бъде избрано това решение, 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. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoError:getaddrinfo ENOTFOUND undefined undefined:27017

  2. попълни в mongodb с meteor

  3. Метод MongoDB Date().

  4. Група драйвери на MongoDB .NET по времеви диапазон

  5. mongodb и mongomapper