mongodb - 子数组中的对象在 mongodb 中过滤查询

标签 mongodb mongodb-query aggregation-framework

我已经为此苦苦挣扎了一段时间,所以切入正题:

我在数据库中有这个对象

{
  topic: [
    {
      topicName: "Reproduction in plants",
      subTopic: ["Pollination", "Other Topic"]
    },
    {
      topicName: "Plant Cycle",
      subTopic: ["Pollination", "Photosynthesis"]
    },
  ]
}

我这里要解决的问题是如何进行满足以下条件的查询:

  1. 查找主题查询范围内的所有主题
  2. 对于每个主题,过滤掉主题内有子主题的结果

假设我想从查询中得到这个:

  1. 获取有关“植物繁殖”的主题,“授粉”子主题
  2. 获取关于“植物周期”的主题,“光合作用”子主题

在我目前的解决方案中:

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/

相关文章:

mongodb - 如何将两个数组连接到对象的字段中

python - 如果 Mongo 中两个日期之间存在任何数据,则返回 true 或 false

Mongodb 组然后是子组

javascript - Mongoose 更新所有文档

javascript - 如何将 sails 连接到 mongodb?

javascript - 如何编写 Mongoose 查询来组合两个模型的数据?

MongoDB 模式设计适用于 Tinder 之类的应用程序、嵌入式文档还是单独的集合?

javascript - MongoDB 聚合 () - 错误 "TypeError: Cannot call method ' forEach' of undefined"

node.js - 使用 MongoDB 在 Node.js 中扩展,我应该什么时候一次查询所有 ID,还是并行查询每个 ID?

javascript - 使用 Mongoose 的 .populate() 返回某些字段