本文为个人在实际项目使用过程中实现复杂查询功能的一些经验积累,不足之处,还望不吝赐教。
mongo-go-driver是mongo官方推出的golang语言的驱动程序,github地址:https://github.com/mongodb/mongo-go-driver
在开始介绍如何实现复杂查询之前,我们有必要先熟悉下BSON。
JSON文档在MongoDB里面以二进制形式存储, 被称作BSON(二进制编码的JSON)。和其它的数据库保存JSON数据为简单的字符串和数字, BSON扩展了JSON的保存形式, 包括额外的类型, 比如int, long, date, floating point以及decimal128。这使得它让应用程序更容易来可靠地处理、排序和比较数据。Go Driver有两个系列的类型表示BSON数据:D系列类型和Raw系列类型。
D系列的类型使用原生的Go类型简单地构建BSON对象。这可以非常有用的来创建传递给MongoDB的命令。
D系列包含4种类型:
– D:一个BSON文档。这个类型应该被用在顺序很重要的场景, 比如MongoDB命令。
– M: 一个无序的map。 它和D是一样的, 除了它不保留顺序。
– A: 一个BSON数组。
– E: 在D里面的一个单一的子项。
在真实项目中需要同时基于多个查询条件进行查询,并且查询条件的个数也是不确定的。具体查询要求描述如下:
1)基于name字段进行模糊查询
2)基于type字段进行完全匹配的查询
3)基于time字段进行范围查询
4)另外上述三个查询字段都可以为空
通常情况下,我们可以通过直接构建bson对象来实现查询,具体语法如下:
func buildQueryFilter(query *entities.QueryEntity) interface{} {
filter := bson.M{
{
"name",
bson.D{{
"$regex",
query.Condition.Type,
}},
},
{
"type",
query.Condition.Type,
},
{
"recordtime",
bson.D{
{
"$gte",
query.Condition.Time.StartTime,
},
{
"$lte",
query.Condition.Time.EndTime,
},
},
},
}
}
上述的查询方案,可以满足前述复杂查询场景的前三个条件,但是如何满足第四个条件,本人暂时没有找到好的解决方案。
虽然此方案可以实现一定的复杂查询,但是个人觉得在使用过程中会存在以下问题:
1)bson对象的构建方法不太友好,尤其是多个字段同时查询,并且查询类型(模糊查询,完全匹配,范围查询)也不一样的时候
2)另外在查询字段不是固定的情况下,如何方便快速的构建出有效的查询条件很麻烦,有时候根本无法实现(个人当前没有找到方案,哪位大牛如果有好的方案,还望不吝赐教)
为了解决第一种方案存在的问题,经过一系列的调研和摸索,终于找到了以下实现方案,敬请指教。
本方案的启发是bson.M对象本身就是一个map,所以我们首先将所有的查询条件构建到一个map[string]interface{}对象中。然后再将这个map对象转换成bson对象。
具体实现方案如下:
1)构建filter对象:
func buildQueryFilter(query *entities.QueryEntity) interface{} {
filterData := make(map[string]interface{})
// 模糊查询
if query.Condition.Name != "" {
filterName := make(map[string]string)
filterName["$regex"] = query.Condition.Name
filterData["name"] = filterName
}
// 完全匹配
if query.Condition.Type != "" {
filterData["type"] = query.Condition.Type
}
// 范围查询
filterTime := make(map[string]string)
if query.Condition.Time.StartTime != "" && query.Condition.Time.EndTime != "" {
filterTime["$gte"] = query.Condition.Time.StartTime
filterTime["$lte"] = query.Condition.Time.EndTime
filterData["recordtime"] = filterTime
}
return convertFilter(filterData)
}
2)转换map对象为bson对象:
func convertFilter(filterData map[string]interface{}) interface{} {
filter := bson.M{}
data, err := bson.Marshal(filterData)
if err != nil {
klog.Errorf("marshal error: %v", err)
return filter
}
err = bson.Unmarshal(data, filter)
if err != nil {
klog.Errorf("unmarshal error: %v", err)
}
klog.Infof("filter: %v", filter)
return filter
}
通过第二种方案,我们能够借助map对象的灵活性,快速方便的实现复杂的查询场景。