子阵列中的对象过滤mongodb

时间:2016-03-22 21:58:59

标签: mongodb mongodb-query aggregation-framework

我已经挣扎了一段时间,所以切入追逐:

我在数据库中有这个对象

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

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

  1. 查找主题查询中的所有主题
  2. 对于每个主题,使用主题内的子主题过滤掉结果
  3. 所以,让我说我想从查询中得到这个:

    1. 获取"植物繁殖"," Pollination"子主题
    2. 获取"光合作用""植物周期"的主题子主题
    3. 在我目前的解决方案中:

      filterQueries['topic.topicName'] = { $in: ["Reproduction in plants", "Plant Cycle"] };
      filterQueries['topic.subTopic'] = { $in: ["Photosynthesis", "Pollination"] };
      

      它很容易满足大多数条件但遇到边缘情况,它也会从" 植物周期"中获取对象。与" 授粉"子主题。

      如何根据上述条件进行查询以执行我想要的操作?

      我将非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

实际上有“两个”问题没有解决这种情况。

第一个是作为单独的参数,没有什么可以说每个属性的值需要在同一个元素内,或者实际上如果特定组合实际出现在该元素中。要解决您在$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"数组因过滤而不是“空”的地方。 / p>

可以在早期版本中执行此操作,但使用$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"]
    }
  ]
}

仅返回任何文档中“已过滤”的匹配元素。