假设我的图书集中有以下数据:
[
{
name: "Animal Farm",
readers: [
{
name: "Johny"
},
{
name: "Lisa"
}
],
likes: [
{
name: "Johny"
}
]
},
{
name: "1984",
readers: [
{
name: "Fred"
},
{
name: "Johny"
},
{
name: "Johny",
type: "bot"
}
],
likes: [
{
name: "Fred"
}
]
}
]
如何检索所有符合名称" Johny"的读者和喜欢,最终结果如下:
[
{
name: "Animal Farm",
readers: [
{
name: "Johny"
}
],
likes: [
{
name: "Johny"
}
]
},
{
name: "1984",
readers: [
{
name: "Johny"
},
{
name: "Johny",
type: "bot"
}
],
likes: []
}
]
无法进行以下查询:
db.books.find(
{ $or: [{ "readers.name": "Johny" }, { "likes.name": "Johny" }] },
{ name: 1, "readers.$": 1, "likes.$": 1 })
MongoDB抱怨以下错误:Cannot specify more than one positional array element per query (currently unsupported).
我曾尝试使用聚合框架,但没有成功。那么MongoDB可以实现这一点,还是必须运行两个查询来检索所需的结果?
答案 0 :(得分:5)
正如Sammaye已指出的那样,目前不支持指定多个位置数组元素。
但是,您可以使用$elemMatch投影运算符来获得所需的结果。 $ elemMatch投影运算符限制数组的内容以包含与$ elemMatch条件匹配的元素:
db.books.find(
{ $or: [{ "readers.name": "Johny" }, { "likes.name": "Johny" }] },
{
readers : { $elemMatch : { name : "Johny" }},
likes : { $elemMatch : { name : "Johny" }}
}
);
修改强>
Altough MongoDB没有内置运营商来做你想做的事情,使用现有的运营商,你可以实现你想要的。但是,拥抱自己,这将是一个漫长的过程:
db.books.aggregate([
// find only documents that have correct "name"
{ $match: { $or: [{ "readers.name": "Johny" }, { "likes.name": "Johny" }]}},
// unwind the documents so we can push them to a array
{ $unwind: '$likes' },
// do a group to conditionally push the values into the array
{ $group : {
_id : '$_id',
likes : {
$push : {
$cond : [
{ $eq : ["$likes.name", "Johny"]},
"$likes",
null
]
}
},
readers : { $first : "$readers" },
name : { $first : "$name" }
}},
// the process is repeated for the readers array
{ $unwind: '$readers' },
{ $group : {
_id : '$_id',
readers : {
$push : {
$cond : [
{ $eq : ["$readers.name", "Johny"]},
"$readers",
null
]
}
},
likes : { $first : "$likes" },
name : { $first : "$name" }
}},
// final step: remove the null values from the arrays
{ $project : {
name : 1,
readers : { $setDifference : [ "$readers", [null] ] },
likes : { $setDifference : [ "$likes", [null] ] },
}}
]);
如您所见,您可以在$push
内使用$cond运算符执行“有条件”$push
。但在小组阶段之后,您的数组将包含null
个值。您必须使用setDifference过滤掉它们。
另请注意,您需要为正在构建的每个阵列执行展开/分组阶段,否则双重展开将复制文档,并且最终会在阵列中显示重复值。
答案 1 :(得分:3)
来自@ ChristianP的答案:
db.books.aggregate(
// So we don't have to random do this to docs we don't need to
{$match: { $or: [{ "readers.name": "Johny" }, { "likes.name": "Johny" }] }},
{$unwind: '$readers'},
{$match: { "readers.name": "Johny" }},
{$unwind: '$likes'},
{$match: { "likes.name": "Johny" }},
{$group: {_id: '$_id', likes: {$push: '$likes'}, readers: {$push: '$readers'}}}
)
这样的东西应该能够做你想要的,在查询中执行此操作的功能被避开,以支持这样做。