我是新手。但是我尝试学习编写查询的最合乎逻辑的方法。
假设我有个收藏,如:
{
"id" : NumberInt(1),
"school" : [
{
"name" : "george",
"code" : "01"
},
{
"name" : "michelangelo",
"code" : "01"
}
],
"enrolledStudents" : [
{
"userName" : "elisabeth",
"code" : NumberInt(21)
}
]
}
{
"id" : NumberInt(2),
"school" : [
{
"name" : "leonarda da vinci",
"code" : "01"
}
],
"enrolledStudents" : [
{
"userName" : "michelangelo",
"code" : NumberInt(25)
}
]
}
我想列出一个
key
的出现及其相应的code
值。例如
key
:michelangelo
为了找到 key 的出现,我写了两个不同的
aggregation
查询:db.test.aggregate([
{$unwind: "$school"},
{$match : {"school.name" : "michelangelo"}},
{$project: {_id: "$id", "key" : "$school.name", "code" : "$school.code"}}
])
和
db.test.aggregate([
{$unwind: "$enrolledStudents"},
{$match : {"enrolledStudents.userName" : "michelangelo"}},
{$project: {_id: "$id", "key" : "$enrolledStudents.userName", "code" : "$enrolledStudents.code"}}
])
这两个查询的结果返回我想要的;
{ "_id" : 1, "key" : "michelangelo", "code" : "01" }
{ "_id" : 2, "key" : "michelangelo", "code" : 25 }
其中一个要在
enrolledStudents
中搜索,另一个要在school
字段中搜索。可以将这两个查询简化为更具逻辑性的查询吗?还是这是唯一的方法?
ps:我知道数据库结构不是逻辑的,但是我尝试进行模拟。
编辑
我尝试用find编写查询。
db.test.find({$or: [{"enrolledStudents.userName" : "michelangelo"} , {"school.name" : "michelangelo"}]}).pretty()
但这将整个文档返回为:
{
"id" : 1,
"school" : [
{
"name" : "george",
"code" : "01"
},
{
"name" : "michelangelo",
"code" : "01"
}
],
"enrolledStudents" : [
{
"userName" : "elisabeth",
"code" : 21
}
]
}
{
"id" : 2,
"school" : [
{
"name" : "leonarda da vinci",
"code" : "01"
}
],
"enrolledStudents" : [
{
"userName" : "michelangelo",
"code" : 25
}
]
}
最佳答案
蒙戈3.4 $match
-此阶段将保留所有school
数组和enrolledStudents
,其中至少有一个嵌入式文档与查询条件都匹配$group
-此阶段将组中每个school
的所有enrolledStudents
和_id
数组组合为2d数组。$project
-此阶段将对$filter
数组进行merge
,以匹配查询条件,并使用新标签$map
数组对values
进行匹配。$unwind
-此阶段将使数组变平。$addFields
和$replaceRoot
-此阶段将添加id
字段,并将values
数组提升到顶部。
db.collection.aggregate([
{$match : {$or: [{"enrolledStudents.userName" : "michelangelo"} , {"school.name" : "michelangelo"}]}},
{$group: {_id: "$id", merge : {$push:{$setUnion:["$school", "$enrolledStudents"]}}}},
{$project: {
values: {
$map:
{
input: {
$filter: {
input: {"$arrayElemAt":["$merge",0]},
as: "onef",
cond: {
$or: [{
$eq: ["$$onef.userName", "michelangelo"]
}, {
$eq: ["$$onef.name", "michelangelo"]
}]
}
}
},
as: "onem",
in: {
key : { $ifNull: [ "$$onem.userName", "$$onem.name" ] },
code : "$$onem.code"}
}
}
}
},
{$unwind: "$values"},
{$addFields:{"values.id":"$_id"}},
{$replaceRoot: { newRoot:"$values"}}
])
sample 响应
{ "_id" : 2, "key" : "michelangelo", "code" : 25 }
{ "_id" : 1, "key" : "michelangelo", "code" : "01" }
Mongo
用
$project
替换上述聚合的最后两个阶段,以格式化响应。{$project: {"_id": 0 , id:"$_id", key:"$values.key", code:"$values.code"}}
sample 响应
{ "_id" : 2, "key" : "michelangelo", "code" : 25 }
{ "_id" : 1, "key" : "michelangelo", "code" : "01" }
您可以使用
$redact
代替$group
和match
,并添加$project
和$map
来格式化响应。$redact
一次遍历文档级别,并根据匹配条件执行$$DESCEND
和$$PRUNE
。唯一需要注意的是在
$ifNull
的第一个文档级别使用id
,以便您可以将$$DESCEND
扩展到嵌入式文档级别以进行进一步处理。db.collection.aggregate([
{
$redact: {
$cond: [{
$or: [{
$eq: ["$userName", "michelangelo"]
}, {
$eq: ["$name", "michelangelo"]
}, {
$ifNull: ["$id", false]
}]
}, "$$DESCEND", "$$PRUNE"]
}
},
{
$project: {
id:1,
values: {
$map:
{
input: {$setUnion:["$school", "$enrolledStudents"]},
as: "onem",
in: {
key : { $ifNull: [ "$$onem.userName", "$$onem.name" ] },
code : "$$onem.code"}
}
}
}
},
{$unwind: "$values"},
{$project: {_id:0,id:"$id", key:"$values.key", code:"$values.code"}}
])