Mgo, как найти вложенный документ внутри вложенных массивов?

Ниже у меня есть структура документа, разработанная следующим образом:

type MIS_Course struct {
    ID   bson.ObjectId `bson:"_id,omitempty"`
    Name string        `bson:"crse_name"`
}

type MIS_Department struct {
    ID      bson.ObjectId `bson:"_id,omitempty"`
    Name    string        `bson:"dept_name"`
    Courses []MIS_Course  `bson:"crse_list"`
}

type MIS_School struct {
    ID          bson.ObjectId    `bson:"_id,omitempty"`
    Name        string           `bson:"school_name"`
    Departments []MIS_Department `bson:"dept_list"`
}

И при инициализации у меня будет «Школьная азбука» в море школ, содержащая что-то вроде следующего:

{
    "_id" : ObjectId("55fbb522022aae7b3a000003"),
    "school_name" : "School ABC",
    "dept_list" : [
        {
            "dept_name" : "Department A",
            "crse_list" : [
                {
                    "crse_name" : "Class A"
                },
                {
                    "crse_name" : "Class B"
                },
            ]
        }
    ]
}

В течение нескольких часов я не мог найти решение, которое бы эффективно работало с учетом имени школы, имени отдела и имени crse:

Найдите dept_list из school_name > найдите crse_list из dept_name > найдите crse_name

Причина, по которой требуется такая цепочка находок, заключается в том, что область находки должна быть ограничена школой, а затем отделом. Логика и уборка происходит после каждого этапа находок.

Я пробовал код, такой как

result := MIS_School{}

err := database.C("schools").Find(bson.M{"school_name": school}).Select(
bson.M{"dept_list": bson.M{"$elemMatch": bson.M{"dept_name": dept}}}).Select(
bson.M{"crse_list": bson.M{"$elemMatch": bson.M{"crse_name": crse}}}).One(&result)

Но это не сработало, так как проекции Select не могут быть объединены в цепочку в Mgo (?)

Я где-то читал, что mongodb не имеет возможности напрямую извлекать документы, вложенные в массивы глубже 1-2 уровней (получение документа C внутри массива B внутри массива A). Это правда? Как я могу обойти эту проблему?

Спасибо!


person William Yang    schedule 18.09.2015    source источник


Ответы (2)


Вы можете связать операторы Select, но значение второго вызова переопределит значение первого вызова, вместо того, чтобы делать то, что следует из примера, то есть углубляться во вложенную структуру.

Чтобы достичь того, что, по-видимому, вы пытаетесь сделать, вы можете использовать структуру агрегации для произвольного управления этими вложенными объектами.

Например, это простой пайплайн, который выведет курсы с точным соответствием:

    pipeline := []bson.M{
            {"$match": bson.M{"school_name": school}},
            {"$unwind": "$dept_list"},
            {"$unwind": "$dept_list.crse_list"},
            {"$match": bson.M{
                     "dept_list.dept_name": dept,
                     "dept_list.crse_list.crse_name": crse,
            }},
    }
    iter := coll.Pipe(pipeline).Iter()

Полученный итератор можно использовать так же, как итераторы из Find.

Для этого конвейера результирующие объекты будут выглядеть так:

    bson.M{
            "_id":"...",
            "dept_list": bson.M{
                    "dept_name": "Department A",   
                    "crse_list": bson.M{
                            "crse_name": "Class B",
                    }
            },
            "school_name":"School ABC",
    }

Однако вы можете произвольно изменить форму результирующего объекта. Дополнительные сведения см. в документации по структуре агрегации.

person Gustavo Niemeyer    schedule 18.09.2015
comment
Хм.... это работает, но, скажем, resp := bson.M{} database.C(schools).Pipe(pipeline).One(&resp) Я мог читать только resp[dept_list], но не resp[dept_list][ crse_list], потому что (интерфейс типа {} не поддерживает индексацию). Я знаю, что есть способы превратить bson.M в массив, а затем проиндексировать его оттуда, но я искал везде и пока не нашел ничего работающего. - person William Yang; 19.09.2015
comment
Посмотрите документацию Go. Поиск утверждений типа. - person Gustavo Niemeyer; 21.09.2015
comment
Превосходно! Я прочитал документацию и заставил ее работать с помощью конвейеров. Но после прочтения документов mongo некоторое время я думаю, что материальные пути/происхождение - это способ реализовать вложенные многоуровневые отношения. - person William Yang; 21.09.2015

После прочтения документов mongo я думаю, что предпочтительным способом создания вложенных отношений является использование древовидных структур с материализованными путями, как описано здесь на монго.

Также доступны деревья из массива предков и родительские/дочерние ссылки. Взглянем.

Кажется, что использование деревьев проще, элегантнее и избавляет от многих технических хлопот.

person William Yang    schedule 21.09.2015