Неструктурированные коллекции MongoDB с mgo

Я ОЧЕНЬ новичок в Go. Из того, что я видел в примерах mGo, чтобы запросить коллекцию и затем прочитать ее, вы должны предопределить данные, которые будут возвращаться в структуру.

type Person struct {
    ID        bson.ObjectId `bson:"_id,omitempty"`
    Name      string
    Phone     string
    Timestamp time.Time
}

В PHP документ был присвоен массиву. Это было прекрасно, так как у одной записи может быть совершенно другой набор ключей (не может содержать имя или телефон, но содержать электронную почту), и я мог бы получить к нему доступ напрямую, не устанавливая предопределенный класс/структуру/переменную.

Есть ли способ сделать то же самое в Go/mGo?

Ответ 1

Существует несколько способов справиться с этим.

Использование карты:

var m bson.M
err := collection.Find(nil).One(&m)
check(err)
for key, value := range m {
    fmt.Println(key, value)
}

Обратите внимание, что нет ничего особенного в bson.M в отношении mgo. Это всего лишь тип map[string]interface{}, и вы можете определить свои собственные типы карт и использовать их с помощью mgo, даже если они имеют другой тип значения.

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

bson.D - это срез, который внутри всего известен mgo, и он существует как для создания более эффективного механизма, так и для способ сохранения порядка ключей, который используется MongoDB в некоторых случаях (например, при определении индексов).

Например:

var d bson.D
err := collection.Find(nil).One(&d)
check(err)
for i, elem := range d {
    fmt.Println(elem.Name, elem.Value)
}

Использование поля inline map

Флаг ,inline bson также может использоваться в поле карты, так что вы можете получить свой торт и съесть его тоже. Другими словами, он позволяет использовать структуру, так что манипулирование известными полями удобен и в то же время позволяет иметь дело с неизвестными полями через встроенную карту.

Например:

type Person struct {
    ID        bson.ObjectId `bson:"_id,omitempty"`
    Name      string
    Phone     string
    Extra     bson.M `bson:",inline"`
}

Ответ 2

Вы можете хранить все на карте. Пакет mgo/bson предоставляет тип данных bson.M, который может использоваться для хранения произвольных данных, и поскольку MongoDB не применяет сильную схему, mgo использует тип bson.M для внутреннего использования.

Если вы просто хотите отображать данные, использование bson.M должно быть прекрасным, но как только вы захотите начать работать с ним, вам следует рассмотреть возможность использования структуры. В противном случае вам понадобится много утверждений типа в вашей программе. Например, подумайте, что вы хотите напечатать заголовок (result["title"]) вашего документа в верхнем регистре. Используя только bson.M, ваш код будет выглядеть так:

// is there a title attribute?
if title, ok := result["title"]; ok {
    // is it a string? (and not another map or integer or something else)
    if titleStr, ok := title.(string); ok {
        // ok, it is a string
        fmt.Println("Title: ", strings.ToUpper(titleStr))
    }
}

Ваша программа станет намного читабельнее и будет проще поддерживать, когда вы позволяете mgo преобразовывать данные в структуру для вас. Затем один и тот же код может выглядеть как:

fmt.Println(strings.ToUpper(result.Title))

Обычно вы определяете один тип структуры для каждого типа документа, с которым вы хотите иметь дело (т.е. один тип для "пользователей", другой для "записей в блоге" и т.д.), который содержит все атрибуты, к которым вы, возможно, захотите получить доступ. Если ваш пользовательский документ не имеет назначенного адреса электронной почты, вы просто получите пустую строку (или, в более общем смысле, нулевое значение), когда вы ее декодируете.