我已经为此苦苦挣扎了一段时间,所以切入正题:
我在数据库中有这个对象
{
topic: [
{
topicName: "Reproduction in plants",
subTopic: ["Pollination", "Other Topic"]
},
{
topicName: "Plant Cycle",
subTopic: ["Pollination", "Photosynthesis"]
},
]
}
我这里要解决的问题是如何进行满足以下条件的查询:
- 查找主题查询范围内的所有主题
- 对于每个主题,过滤掉主题内有子主题的结果
假设我想从查询中得到这个:
- 获取有关“植物繁殖”的主题,仅“授粉”子主题
- 获取关于“植物周期”的主题,仅“光合作用”子主题
在我目前的解决方案中:
filterQueries['topic.topicName'] = { $in: ["Reproduction in plants", "Plant Cycle"] };
filterQueries['topic.subTopic'] = { $in: ["Photosynthesis", "Pollination"] };
它很容易满足大多数条件,但遇到了边缘情况,它还会从“Plant Cycle”和“Pollination”子主题中获取对象。
如何根据上述条件进行查询以执行我想要的操作?
我将非常感谢任何帮助。
最佳答案
实际上有“两个”问题没有用这种条件解决。
首先,作为单独的参数,没有什么可以说每个属性的值需要在同一元素内,或者事实上如果特定组合实际出现在该元素中。要补救,您可以使用 $elemMatch
$or
内的条件表达式:
var query = {
"$or": [
{ "topic": {
"$elemMatch": {
"topicName": "Reproduction in plants",
"subTopic": "Pollination"
}
}},
{ "topic": {
"$elemMatch": {
"topicName": "Plant Cycle",
"subTopic": "Photosynthesis"
}
}}
]
}
至少选择具有您需要的元素组合的文档。
但是其中没有任何内容可以“过滤”来自外部 “topic”
或“内部”“subTopic”
数组的多个结果。对于该任务,您需要聚合框架,因为基本投影无法使用此类功能:
var pipeline = [
// Still use the same match to filter possible documents
{ "$match": {
"$or": [
{ "topic": {
"$elemMatch": {
"topicName": "Reproduction in plants",
"subTopic": "Pollination"
}
}},
{ "topic": {
"$elemMatch": {
"topicName": "Plant Cycle",
"subTopic": "Photosynthesis"
}
}}
]
}},
// Filter the arrays for matches
{ "$project": {
"topics": {
"$filter": {
"input": {
"$map": {
"input": "$topic",
"as": "topic",
"in": {
"topicName": "$$topic.topicName",
"subTopic": {
"$filter": {
"input": "$$topic.subTopic",
"as": "subTopic",
"cond": {
"$or": [
{ "$and": [
{ "$eq": [ "$$topic.topicName", "Reproduction in plants" ] },
{ "$eq": [ "$$subTopic", "Pollination" ] }
]},
{ "$and": [
{ "$eq": [ "$$topic.topicName", "Plant Cycle" ] },
{ "$eq": [ "$$subTopic", "Photosynthesis" ] }
]}
]
}
}
}
}
}
},
"as": "topic",
"cond": {
"$and": [
{ "$or": [
{ "$eq": [ "$$topic.topicName", "Reproduction in plants" ] },
{ "$eq": [ "$$topic.topicName", "Plant Cycle" ] }
]},
{ "$ne": [ "$$topic.subTopic", [] ] }
]
}
}
}
}}
];
// API call to aggregate
Model.aggregate(pipeline,function(err,results) {
// results in here
});
这是使用 $filter
的 MongoDB 3.2 的最佳方法。对数组的操作。因此,首先您会注意到正在测试内部 "subTopic"
元素是否与外部元素匹配以决定返回哪些元素。这被放置在 $map
中,以便将“过滤”的内容返回到外部数组属性以供进一步检查。
然后“过滤”外部数组,以便只返回匹配的 “topicName”
值,当然只有 “subTopic”
数组不是“空”作为过滤的结果。
可以在早期版本中执行此操作,但使用 $unwind
的典型过程变得非常长且昂贵:
{ "$unwind": "$topic" },
{ "$unwind": "$topic.subTopic" },
{ "$match": {
"$or": [
{
"topic.topicName": "Reproduction in plants",
"topic.subTopic": "Pollination"
},
{
"topic.topicName": "Plant Cycle",
"topic.subTopic": "Photosynthesis"
}
]
}},
{ "$group": {
"_id": {
"_id": "$_id",
"topicName": "$topic.topicName",
},
"subTopic": { "$push": "$topic.subTopic" }
}},
{ "$group": {
"_id": "$_id._id",
"topic": {
"$push": {
"topicName": "$_id.topicName",
"subTopic": "$_id.subTopic"
}
}
}}
虽然它看起来更容易遵循,但由于 $unwind
的性质,它的成本要很多。当然,添加的每个聚合管道阶段都有其自己的处理成本,而现代版本可以在一个简单的 $project
中完成。 .
如果您有较早的版本,最好的办法是使用前面提到的初始“查询”,同时使用 $or
和 $elemMatch
,然后执行代码中的数组过滤。
当然,除非您确实需要在聚合管道中进一步处理该数据,否则您将在该流程中受困以“过滤”。
无论如何,你会得到的结果:
{
"topic": [
{
"topicName": "Reproduction in plants",
"subTopic": ["Pollination"]
},
{
"topicName": "Plant Cycle",
"subTopic": ["Photosynthesis"]
}
]
}
它只返回任何文档中“过滤”的那些匹配元素。
关于mongodb - 子数组中的对象在 mongodb 中过滤查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36166041/