golang 整合mongo-driver 复杂文档查找aggregate 遇到的问题

golang 整合mongo-driver 复杂文档查找aggregate 遇到的问题

现在手头有这样一个文档,用来存储心理测试问卷

{
    "_id":"3231231241243141",
    "typename":"心理健康测试",
    "mental_tests":[{
        "testname":"艾森克成人测试",
        "describe":"作用1",
        "probles":[{
            "context":"问题1",
            "options":[{
                "title":"是"
            },{
                "title":"否"
            }]
        },{
            "context":"问题2",
            "options":[{
                "title":"是"
            },{
                "title":"否"
            },{
                "title":"一般"
            }]
        }]
    },{
        "testname":"xxx测试",
        "describe":"作用1",
        "probles":[{
            "context":"问题3",
            "options":[{
                "title":"是"
            },{
                "title":"否"
            }]
        }
    }]
}

字段含义:

  • typename:心理测试种类
  • mental_tests 该种测试的测试集合,类型为Array
  • mental_tests.testname 测试问卷的名称
  • mental_tests.describe 测试问卷作用描述
  • mental_tests.probles 问卷问题集合
  • mental_tests.probles.context 问卷问题题目
  • mental_tests.probles.options 问卷问题选项集合

需求:

需要根据测试种类Id,获得该种类下的所有测试问卷名称以及描述

目标:

{
    "code": "200",
    "data": [
        {
            "testname": "艾森克成人测试",
            "describe": "作用1"
        },
        {
            "testname": "xxx测试",
            "describe": "作用2"
        }
    ]
}

data 表示我们要查询的结果

本人刚刚入门MongoDB,开发过程中磕磕绊绊的,最中以很迷惑的方法达成了效果,这边列出几个我到目前还不能解决的疑问

使用aggregate聚合过滤出我们需要的文档

说实话我觉得这个过程挺难的,由于mongoDB并不是太了解,仅仅看了极客世界的一节课就上手了,我是这样实现的

  • 首先$match 查找出_id=传入参数的ObjectId
    在这里插入图片描述
    这边有个点,前端传入的_id为string类型,我们需要将string类型转化为MongoDB中的Object(‘Id’),当然是我查过来的
objectId, err := primitive.ObjectIDFromHex(id)

其中ObjectIDFromHex()方法,在primitive包下,传入一个string,返回一个ObjectId

func ObjectIDFromHex(s string) (ObjectID, error) {
	if len(s) != 24 {
		return NilObjectID, ErrInvalidHex
	}

	b, err := hex.DecodeString(s)
	if err != nil {
		return NilObjectID, err
	}

	var oid [12]byte
	copy(oid[:], b)

	return oid, nil
}

ObjectId是一个byte数组

// ObjectID is the BSON ObjectID type.
type ObjectID [12]byte
  • 通过 $project 过滤出我们需要的字段,
    _id :0表示 _id字段不显示
    在这里插入图片描述
    这时候我们就查找出了一个Array,其中包含了问卷的信息
    在这里插入图片描述
    在golang中的代码
    在这里插入图片描述

问题出现

我的问题也就在这边出现了,根据上边查询的结果,我们手头应该是一个列表,包含了两张问卷的信息,结合我们的需求,我们不需要problem字段,而是需要一个列表,列表中的每一项包含了测试问卷的名称,以及描述

于是我定义了这么两个结构体

type TestInfo struct {   //用于存放每一个元素
	TestName string `bson:"testname" json:"testname"`
	Describe string `bson:"describe" json:"describe"`
}

type TestInfoDto struct { //存放查询的结果
	TestInfos []TestInfo `bson:"mental_tests"`
}

那么按照我的理解,于是有了以下的代码:

type TestInfo struct {
	TestName string `bson:"testname" json:"mental_tests.testname"`
	Describe string `bson:"describe" json:"mental_tests.describe"`
}

type TestInfoDto struct {
	TestInfos []TestInfo `bson:"mental_tests"`
}

接口整体代码:

func FindTestsByType(id string) (dto.TestInfoDto, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	//选取表
	collection := config.Client.Database(config.Conf.Mongo.Name).Collection("mental")
	objectId, err := primitive.ObjectIDFromHex(id)
	matchStage := bson.D{
		{"$match", bson.M{"_id": objectId}},
	}
	projectStage := bson.D{
		{"$project", bson.D{{"mental_tests", "$mental_tests"}, {"_id", 0}}},
	}
	//查询结果为我们TestInfoDto结构体
	var result dto.TestInfoDto
	cur, err := collection.Aggregate(ctx, mongo.Pipeline{matchStage, projectStage})
	if err != nil {
		log.Println(err)
		return dto.TestInfoDto{}, err
	}
	//将结果写入到result中
	err = cur.Decode(&result)
	if err != nil {
		log.Println(err)
		return dto.TestInfoDto{}, err
	}
	if err != nil {
		log.Println(err)
		return dto.TestInfoDto{}, err
	}
	return result, err
}

Controller层代码,直接返回FindTestsByType找到的TestInfoDto,包含了一个TestInfo切片
在这里插入图片描述

按常理来说,这边感觉是真没问题呀,不知道有什么坑,报错EOF,怀疑是problem字段没有写(可能),有没有巨巨可以告诉我为什么

在这里插入图片描述

我的解决方法

很捉鸡,很烂的写法,我甚至不知道为什么可以

//将返回参数设置为TestInfoDto切片
func FindTestsByType(id string) ([]dto.TestInfoDto, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	collection := config.Client.Database(config.Conf.Mongo.Name).Collection("mental")
	objectId, err := primitive.ObjectIDFromHex(id)
	matchStage := bson.D{
		{"$match", bson.M{"_id": objectId}},
	}
	projectStage := bson.D{
		{"$project", bson.D{{"mental_tests", "$mental_tests"}, {"_id", 0}}},
	}
	var result []dto.TestInfoDto
	cur, err := collection.Aggregate(ctx, mongo.Pipeline{matchStage, projectStage})
	if err != nil {
		log.Println(err)
		return nil, err
	}
	//将查询结果以切片方式写入到result中
	err = cur.All(ctx, &result)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	if err != nil {
		log.Println(err)
		return nil, err
	}
	//返回结果
	return result, err
}

Controller层代码:
在这里插入图片描述
由于Tests是一个TypeInfoDto切片,根据_Id查询出的结果肯定只有第一项,所以就直接投机取巧了,拿到Array中的数据

在这里插入图片描述

总结:就写这么一个简单的结构,换成MySQL几分钟就搞定了。还是MongoDB不太熟悉,还有好多好多不会,把我心态搞裂开了,对于我的问题,希望看到的大佬能帮我解答一下,非常感谢!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值