英文:
MongoDB Aggregate lookup in Go (mgo.v2)
问题
我正在尝试在使用mgo包的go(golang)中实现$lookup
功能的一个mongoDB查询中进行实现。
以下是我的集合:
folders:
"_id" : ObjectId("22222222222222"),
"name" : "Media",
"level" : 1,
"userIDs": [ObjectId("4444444444444")]
documents:
"_id" : ObjectId("11111111111111"),
"title" : "Media Management",
"body" : BinData(0,"PvQ6z2NBm4265duo/e2XsYxA5bXKo="),
"level" : 1,
"folderID" : ObjectId("22222222222222"), // 外键/字段
"userIDs" : [ObjectId("44444444444444")]
以下是我在shell上成功运行的查询:
var query = [
{
"$lookup": {
"from": "documents",
"localField": "_id",
"foreignField": "folderID",
"as": "documents",
}
}
,{
"$match": {
"userIDs": ObjectId("userIdHere"), // 根据userID过滤
"level": {$gte: 0}, // 根据文件夹级别过滤
},
}
];
db.folders.aggregate(query).pretty().shellPrint();
如果我在shell上运行此脚本,我会得到所需的结果。基本上,返回给我的是包含通过$lookup
链接的完整相关documents
的folder
集合。我没有在这里包含它,因为这个问题已经太长了。
我尝试将此查询转换为mgo能够解析和执行的内容。以下是go代码中的查询:
query := bson.M{
"$lookup": bson.M{ // 在这里查找documents表
"from": "documents",
"localField": "_id",
"foreignField": "folderID",
"as": "documents",
},
"$match": bson.M{
"level": bson.M{"$gte": user.Level}, // 根据级别过滤
"userIDs": user.ID, // 根据用户过滤
},
}
pipe := collection.Pipe(query) // 查询"folders"集合
err := pipe.All(&result)
我始终收到相同的错误:字段的类型错误(pipeline)3 != 4
如果我理解正确,这是因为它无法正确解析结果返回到result
对象中。我已经尽一切努力确保结构具有所需的确切结构。我还尝试传递一个通用的[]interface{}
和一个空的bson.M{}
对象。仍然收到相同的错误。
以下是我的Folders结构:
type Folder struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Name string `json:"name"`
Level int `json:"level"`
UserIDs []bson.ObjectId `json:"userIDs" bson:"userIDs"`
Users []User `json:"-" bson:"-"`
Documents []Document `json:"-" bson:"-"` // 不存储在数据库中
}
我还删除了$match
子句,以查看是否可以从$lookup
查询中获取任何内容。但我仍然收到相同的错误。
也许mgo包不支持$lookup
?如果是这样,是否有其他方法?也许我可以将原始查询文本发送到mongo并接收原始响应并自行解析?
英文:
I'm trying to implement $lookup
functionality in one of my mongoDB queries in go (golang) using the mgo package.
Below are my collections:
folders:
"_id" : ObjectId("22222222222222"),
"name" : "Media",
"level" : 1,
"userIDs": [ObjectId("4444444444444")]
documents:
"_id" : ObjectId("11111111111111"),
"title" : "Media Management",
"body" : BinData(0,"PvQ6z2NBm4265duo/e2XsYxA5bXKo="),
"level" : 1,
"folderID" : ObjectId("22222222222222"), // Foreign Key/Field
"userIDs" : [ObjectId("44444444444444")]
Below is the query I've written that successfully runs on the shell:
var query = [
{
"$lookup": {
"from": "documents",
"localField": "_id",
"foreignField": "folderID",
"as": "documents",
}
}
,{
"$match": {
"userIDs": ObjectId("userIdHere"), // filder by a userID
"level": {$gte: 0}, // filter by a folder level
},
}
];
db.folders.aggregate(query).pretty().shellPrint();
If I run this script on the shell, I get the desired result. Basically, the folder
collection is returned to me containing the full relevant documents
that were linked through the $lookup
. I'm not including it here because this question already seems too long.
I've tried to translate this query into something that mgo would be able to parse and execute. Here it is below in go code:
query := bson.M{
"$lookup": bson.M{ // lookup the documents table here
"from": "documents",
"localField": "_id",
"foreignField": "folderID",
"as": "documents",
},
"$match": bson.M{
"level": bson.M{"$gte": user.Level}, // filter by level
"userIDs": user.ID, // filter by user
},
}
pipe := collection.Pipe(query) // querying the "folders" collection
err := pipe.All(&result)
I always get the same error: wrong type for field (pipeline) 3 != 4
If I understand correctly, it's because it can't properly parse the result back into the $result object. I've done everything I can to ensure the struct has the exact structure that is required. I've also tried to pass in a genereric []interface{}
and an empty bson.M{}
objects. Still receive the same error.
Below is my Folders struct:
type Folder struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Name string `json:"name"`
Level int `json:"level"`
UserIDs []bson.ObjectId `json:"userIDs" bson:"userIDs"`
Users []User `json:"-" bson:"-"` // doesn't get stored in the database
Documents []Document `json:"-" bson:"-"` // doesn't get stored in the database
}
I've also removed the $match
clause to see if I could get anything at all back from that $lookup
query. But I still get the same error.
Perhaps the mgo package doesn't support $lookup
? If so, would there be another way? Perhaps I could send the raw query text to mongo and receive the raw response and parse it myself?
答案1
得分: 8
找到解决方案!
关键是在一个切片([]bson.M
)中创建查询,并稍微改变查询的结构:
query := []bson.M{{
"$lookup": bson.M{ // 在这里查找documents表
"from": "documents",
"localField": "_id",
"foreignField": "folderID",
"as": "documents",
}},
{"$match": bson.M{
"level": bson.M{"$lte": user.Level},
"userIDs": user.ID,
}},
}
pipe := collection.Pipe(query)
err := pipe.All(&folders)
我在mgo的Pipe函数文档中找到了一些线索。此外,我还必须更改Folders
结构中Documents
字段的标签,以便mgo可以填充该字段:
type Folder struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Name string `json:"name"`
Level int `json:"level"`
UserIDs []bson.ObjectId `json:"userIDs" bson:"userIDs"`
Users []User `json:"-" bson:"-"`
Documents []Document // `json:"-" bson:"-"` 我移除了这部分,以便mgo可以正确地解组文档
}
现在我只需要找到一种方法,在保存Folder
时不将Documents
字段存储在数据库中。
英文:
Found the solution!
The trick was to create the query in a slice ([]bson.M
) and change the structure of the query a bit:
query := []bson.M{{
"$lookup": bson.M{ // lookup the documents table here
"from": "documents",
"localField": "_id",
"foreignField": "folderID",
"as": "documents",
}},
{"$match": bson.M{
"level": bson.M{"$lte": user.Level},
"userIDs": user.ID,
}}}
pipe := collection.Pipe(query)
err := pipe.All(&folders)
I found a clue in mgo's Pipe function docs. Also, I had to change the tags for the Documents
field in my Folders
struct for mgo to pupolate that field:
type Folder struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Name string `json:"name"`
Level int `json:"level"`
UserIDs []bson.ObjectId `json:"userIDs" bson:"userIDs"`
Users []User `json:"-" bson:"-"` // doesn't get stored in the database
Documents []Document // `json:"-" bson:"-" Removed this so that mgo can unmarshal
// the documents correctly
}
Now I just have to figure out a way to not store the Documents
field in the database when I save a Folder
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论