在使用 MongoDB 和 Golang 的查找引用中获取值
Get a value in a reference of a lookup with MongoDB and Golang
我有以下结构。我使用 Golang 1.9.2.
// EventBoost describes the model of a EventBoost
type EventBoost struct {
ID string `bson:"_id" json:"_id" valid:"alphanum,printableascii"`
CampaignID string `bson:"_campaign_id" json:"_campaign_id" valid:"alphanum,printableascii"`
Name string `bson:"name" json:"name"`
Description string `bson:"description" json:"description"`
Level string `bson:"level" json:"level"`
EventID string `bson:"_event_id" json:"_event_id" valid:"alphanum,printableascii"`
StartDate time.Time `bson:"start_date" json:"start_date"`
EndDate time.Time `bson:"end_date" json:"end_date"`
IsPublished bool `bson:"is_published" json:"is_published"`
CreatedBy string `bson:"created_by" json:"created_by"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
ModifiedAt time.Time `bson:"modified_at" json:"modified_at"`
}
// LocationBoost describes the model of a LocationBoost
type LocationBoost struct {
ID string `bson:"_id" json:"_id" valid:"alphanum,printableascii"`
CampaignID string `bson:"_campaign_id" json:"_campaign_id" valid:"alphanum,printableascii"`
Name string `bson:"name" json:"name"`
Description string `bson:"description" json:"description"`
Level string `bson:"level" json:"level"`
LocationID string `bson:"_location_id" json:"_location_id" valid:"alphanum,printableascii"`
StartDate time.Time `bson:"start_date" json:"start_date"`
EndDate time.Time `bson:"end_date" json:"end_date"`
IsPublished bool `bson:"is_published" json:"is_published"`
CreatedBy string `bson:"created_by" json:"created_by"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
ModifiedAt time.Time `bson:"modified_at" json:"modified_at"`
}
// Campaign describes the model of a Campaign
type Campaign struct {
ID string `bson:"_id" json:"_id" valid:"alphanum,printableascii"`
Name string `bson:"name" json:"name"`
Description string `bson:"description" json:"description"`
EventBoostIDs []string `bson:"event_boost_ids" json:"event_boost_ids"`
LocationBoostIDs []string `bson:"location_boost_ids" json:"location_boost_ids"`
StartDate time.Time `bson:"start_date" json:"start_date"`
EndDate time.Time `bson:"end_date" json:"end_date"`
IsPublished bool `bson:"is_published" json:"is_published"`
CreatedBy string `bson:"created_by" json:"created_by"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
ModifiedAt time.Time `bson:"modified_at" json:"modified_at"`
}
Campaign(了解营销活动)由 Events 或 Locations 组成可以提升一个级别(基本或高级)。广告系列有开始日期和结束日期,所以有提升。
函数 GetEventLevel
必须 return 我给定事件的级别。
// GetEventLevel of an event
func (dao *campaignDAO) GetEventLevel(eventID string) (string, error) {
}
如果事件在活动活动中提升(isPublished
是true
),并且提升活动(isPublished
是 true
)并且 现在的日期介于提升的开始日期和结束日期之间,那么我的事件是 boosted,所以函数 returns 级别(基本或高级)。否则,它 returns "standard"
.
我的问题是:我可以用 Mongo 完全做到这一点吗?或者我是否需要使用 Golang 在 DAO 中执行一些逻辑?
如果我可以用 Mongo 做到这一点,但我不知道该怎么做。据我了解,我首先需要查找活动的事件和地点,然后在其中搜索日期,但是..
由于您只是存储 ID 以引用其他集合中的文档,而不是完全去规范化数据,不,您不能纯粹在 MongoDB 中执行此操作。 MongoDB 不是关系数据库。您所描述的正是 MongoDB 设计 而不是 的目的。
您需要在 Go 中执行逻辑;您是否在 DAO 中这样做取决于您,但就个人而言,我倾向于使用一种简单的方法来根据字段值动态执行逻辑,例如Campaign.GetEventLevel
或类似的东西。在你的 DAO 中使用细粒度的方法对我来说意味着一些不适合 MongoDB 模型的不寻常的设计决策。在大多数情况下,使用 MongoDB,您想要检索文档(对象)并在您的应用程序中使用它们。尝试像使用典型的 RDBMS 一样在 MongoDB 中执行查询逻辑将导致挫折和性能不佳。
大多数(也是最难的部分)你想做的事情可以在 MongoDB 中轻松完成。返回 "basic"、"premium" 或 "standard" 时的最后一步很可能也可以完成,但我认为这不值得麻烦,因为这在 Go 中是微不足道的。
在 MongoDB 中使用 Aggregation framework for this. This is available in the mgo
package via the Collection.Pipe()
method. You have to pass a slice to it, each element corresponds to an aggregation stage. Read this answer for more details:
回到你的例子。您的 GetEventLevel()
方法可以这样实现:
func (dao *campaignDAO) GetEventLevel(eventID string) (string, error) {
c := sess.DB("").C("eventboosts") // sess represents a MongoDB Session
now := time.Now()
pipe := c.Pipe([]bson.M{
{
"$match": bson.M{
"_event_id": eventID, // Boost for the specific event
"is_published": true, // Boost is active
"start_date": bson.M{"$lt": now}, // now is between start and end
"end_date": bson.M{"$gt": now}, // now is between start and end
},
},
{
"$lookup": bson.M{
"from": "campaigns",
"localField": "_campaign_id",
"foreignField": "_id",
"as": "campaign",
},
},
{"$unwind": "$campaign"},
{
"$match": bson.M{
"campaign.is_published": true, // Attached campaign is active
},
},
})
var result []*EventBoost
if err := pipe.All(&result); err != nil {
return "", err
}
if len(result) == 0 {
return "standard", nil
}
return result[0].Level, nil
}
如果你最多只需要一个EventBoost
(或者可能不会同时有多个),使用$limit
阶段将结果限制为单个,并使用$project
只获取 level
字段,仅此而已。
使用此管道进行上述简化/优化:
pipe := c.Pipe([]bson.M{
{
"$match": bson.M{
"_event_id": eventID, // Boost for the specific event
"is_published": true, // Boost is active
"start_date": bson.M{"$lt": now}, // now is between start and end
"end_date": bson.M{"$gt": now}, // now is between start and end
},
},
{
"$lookup": bson.M{
"from": "campaigns",
"localField": "_campaign_id",
"foreignField": "_id",
"as": "campaign",
},
},
{"$unwind": "$campaign"},
{
"$match": bson.M{
"campaign.is_published": true, // Attached campaign is active
},
},
{"$limit": 1}, // Fetch at most 1 result
{
"$project": bson.M{
"_id": 0, // We don't even need the EventBoost's ID
"level": "$level", // We do need the level and nothing more
},
},
})
我有以下结构。我使用 Golang 1.9.2.
// EventBoost describes the model of a EventBoost
type EventBoost struct {
ID string `bson:"_id" json:"_id" valid:"alphanum,printableascii"`
CampaignID string `bson:"_campaign_id" json:"_campaign_id" valid:"alphanum,printableascii"`
Name string `bson:"name" json:"name"`
Description string `bson:"description" json:"description"`
Level string `bson:"level" json:"level"`
EventID string `bson:"_event_id" json:"_event_id" valid:"alphanum,printableascii"`
StartDate time.Time `bson:"start_date" json:"start_date"`
EndDate time.Time `bson:"end_date" json:"end_date"`
IsPublished bool `bson:"is_published" json:"is_published"`
CreatedBy string `bson:"created_by" json:"created_by"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
ModifiedAt time.Time `bson:"modified_at" json:"modified_at"`
}
// LocationBoost describes the model of a LocationBoost
type LocationBoost struct {
ID string `bson:"_id" json:"_id" valid:"alphanum,printableascii"`
CampaignID string `bson:"_campaign_id" json:"_campaign_id" valid:"alphanum,printableascii"`
Name string `bson:"name" json:"name"`
Description string `bson:"description" json:"description"`
Level string `bson:"level" json:"level"`
LocationID string `bson:"_location_id" json:"_location_id" valid:"alphanum,printableascii"`
StartDate time.Time `bson:"start_date" json:"start_date"`
EndDate time.Time `bson:"end_date" json:"end_date"`
IsPublished bool `bson:"is_published" json:"is_published"`
CreatedBy string `bson:"created_by" json:"created_by"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
ModifiedAt time.Time `bson:"modified_at" json:"modified_at"`
}
// Campaign describes the model of a Campaign
type Campaign struct {
ID string `bson:"_id" json:"_id" valid:"alphanum,printableascii"`
Name string `bson:"name" json:"name"`
Description string `bson:"description" json:"description"`
EventBoostIDs []string `bson:"event_boost_ids" json:"event_boost_ids"`
LocationBoostIDs []string `bson:"location_boost_ids" json:"location_boost_ids"`
StartDate time.Time `bson:"start_date" json:"start_date"`
EndDate time.Time `bson:"end_date" json:"end_date"`
IsPublished bool `bson:"is_published" json:"is_published"`
CreatedBy string `bson:"created_by" json:"created_by"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
ModifiedAt time.Time `bson:"modified_at" json:"modified_at"`
}
Campaign(了解营销活动)由 Events 或 Locations 组成可以提升一个级别(基本或高级)。广告系列有开始日期和结束日期,所以有提升。
函数 GetEventLevel
必须 return 我给定事件的级别。
// GetEventLevel of an event
func (dao *campaignDAO) GetEventLevel(eventID string) (string, error) {
}
如果事件在活动活动中提升(isPublished
是true
),并且提升活动(isPublished
是 true
)并且 现在的日期介于提升的开始日期和结束日期之间,那么我的事件是 boosted,所以函数 returns 级别(基本或高级)。否则,它 returns "standard"
.
我的问题是:我可以用 Mongo 完全做到这一点吗?或者我是否需要使用 Golang 在 DAO 中执行一些逻辑?
如果我可以用 Mongo 做到这一点,但我不知道该怎么做。据我了解,我首先需要查找活动的事件和地点,然后在其中搜索日期,但是..
由于您只是存储 ID 以引用其他集合中的文档,而不是完全去规范化数据,不,您不能纯粹在 MongoDB 中执行此操作。 MongoDB 不是关系数据库。您所描述的正是 MongoDB 设计 而不是 的目的。
您需要在 Go 中执行逻辑;您是否在 DAO 中这样做取决于您,但就个人而言,我倾向于使用一种简单的方法来根据字段值动态执行逻辑,例如Campaign.GetEventLevel
或类似的东西。在你的 DAO 中使用细粒度的方法对我来说意味着一些不适合 MongoDB 模型的不寻常的设计决策。在大多数情况下,使用 MongoDB,您想要检索文档(对象)并在您的应用程序中使用它们。尝试像使用典型的 RDBMS 一样在 MongoDB 中执行查询逻辑将导致挫折和性能不佳。
大多数(也是最难的部分)你想做的事情可以在 MongoDB 中轻松完成。返回 "basic"、"premium" 或 "standard" 时的最后一步很可能也可以完成,但我认为这不值得麻烦,因为这在 Go 中是微不足道的。
在 MongoDB 中使用 Aggregation framework for this. This is available in the mgo
package via the Collection.Pipe()
method. You have to pass a slice to it, each element corresponds to an aggregation stage. Read this answer for more details:
回到你的例子。您的 GetEventLevel()
方法可以这样实现:
func (dao *campaignDAO) GetEventLevel(eventID string) (string, error) {
c := sess.DB("").C("eventboosts") // sess represents a MongoDB Session
now := time.Now()
pipe := c.Pipe([]bson.M{
{
"$match": bson.M{
"_event_id": eventID, // Boost for the specific event
"is_published": true, // Boost is active
"start_date": bson.M{"$lt": now}, // now is between start and end
"end_date": bson.M{"$gt": now}, // now is between start and end
},
},
{
"$lookup": bson.M{
"from": "campaigns",
"localField": "_campaign_id",
"foreignField": "_id",
"as": "campaign",
},
},
{"$unwind": "$campaign"},
{
"$match": bson.M{
"campaign.is_published": true, // Attached campaign is active
},
},
})
var result []*EventBoost
if err := pipe.All(&result); err != nil {
return "", err
}
if len(result) == 0 {
return "standard", nil
}
return result[0].Level, nil
}
如果你最多只需要一个EventBoost
(或者可能不会同时有多个),使用$limit
阶段将结果限制为单个,并使用$project
只获取 level
字段,仅此而已。
使用此管道进行上述简化/优化:
pipe := c.Pipe([]bson.M{
{
"$match": bson.M{
"_event_id": eventID, // Boost for the specific event
"is_published": true, // Boost is active
"start_date": bson.M{"$lt": now}, // now is between start and end
"end_date": bson.M{"$gt": now}, // now is between start and end
},
},
{
"$lookup": bson.M{
"from": "campaigns",
"localField": "_campaign_id",
"foreignField": "_id",
"as": "campaign",
},
},
{"$unwind": "$campaign"},
{
"$match": bson.M{
"campaign.is_published": true, // Attached campaign is active
},
},
{"$limit": 1}, // Fetch at most 1 result
{
"$project": bson.M{
"_id": 0, // We don't even need the EventBoost's ID
"level": "$level", // We do need the level and nothing more
},
},
})