Сначала давайте быстро объясним ваше поле createdAt
.Это значение, которое вы сохраняете: 2018-05-26T15:08:56.764453386+03:00
.Знайте, что MongoDB хранит даты с точностью до миллисекунды и в часовом поясе UTC.Таким образом, эта дата при сохранении и извлечении из MongoDB становится 2018-05-26T12:08:56.764Z
, это "тот же" момент времени, только в зоне UTC и точность усекается до миллисекунд.
Теперь перейдем к обновлению встроенных документов:
Короткий и неудачный ответ заключается в том, что мы не можем сделать это напрямую с библиотекой mgo
и моделями Go.
Почему?
Когда мы используем опцию ,omitempty
, имы оставляем некоторые поля указателя в их нулевом значении (то есть, будучи nil
), это как если бы мы использовали значение, тип которого даже не имел этих полей.
Так что в вашем примере, если выИзмените только поле BasicInfo.FirstName
, и вы используете это значение для обновления, оно эквивалентно использованию следующих структур:
type UserModel struct {
Id string `bson:"_id,omitempty"`
BasicInfo *UserBasicInfoModel `bson:"basicInfo,omitempty"`
}
type UserBasicInfoModel struct {
FirstName *string `bson:"firstName,omitempty"`
}
Таким образом, эффект от введенной вами команды update
будет следующим:
db.users.update({_id: "aba19b45-5e84-55e0-84f8-90fad41712f6"},
{$set:{
"_id": "aba19b45-5e84-55e0-84f8-90fad41712f6",
"basicInfo": {
"firstName": "New Value"
}
}}
)
Что это значит?Чтобы установить для _id
то же значение (оно не изменится), а для поля basicInfo
- встроенный документ, который имеет только одно свойство firstName
. Это приведет к удалению поля lastName
внедренного документа basicInfo
. Таким образом, когда вы отменяете маршализацию документа после обновления в значение вашего типа UserModel
, поле LastName
останется nil
(поскольку его больше нет в MongoDB).
Что мы можем сделать?
Свести встроенный документ
Одно из тривиальных решений - не использовать внедренный документ,но добавьте поля UserBasicInfoModel
в UserModel
:
type UserModel struct {
Id string `bson:"_id,omitempty"`
CreatedAt *time.Time `bson:"createdAt,omitempty"`
FirstName *string `bson:"firstName,omitempty"`
LastName *string `bson:"lastName,omitempty"`
}
Гибрид с опцией ,inline
Это решение сохраняет отдельную структуру Go, но в MongoDB она не будет встроеннойдокумент (BasicInfo
будет сведен так же, как в предыдущем примере):
type UserModel struct {
Id string `bson:"_id,omitempty"`
CreatedAt *time.Time `bson:"createdAt,omitempty"`
BasicInfo UserBasicInfoModel `bson:"basicInfo,omitempty,inline"`
}
Обратите внимание, что BasicInfo
должен быть не указателем, если используется ,inline
.Это не проблема, так как мы можем оставить его пустой структурой, если его поля не должны быть изменены, так как его поля являются указателями, поэтому оставляя их nil
не изменит их.
Doing "manual"update
Если вам нужно использовать встроенный документ, библиотека mgo
позволяет обновлять определенные поля встроенных документов, но затем вам нужно" вручную "создать документ обновления, как в этом примере:
c.UpdateId(Id, bson.M{"$set": bson.M{
"basicInfo.firstName": newFirstName,
}})
Да, это совсем не удобно.Если вам это нужно много раз для разных типов, вы можете создать служебную функцию, которая использует отражение, рекурсивно перебирает поля и собирает документ обновления из полей, которые не являются nil
.Затем вы можете передать этот динамически сгенерированный документ обновления, например, на UpdateId()
.