Project on specific elements in a nested array

时间:2017-10-03 11:56:59

标签: mongodb mongodb-query

I have a document like this..

{
    "_id" : ObjectId("59b0ea9b3a91af574a3e0464"),
    "machineID" : "b83c",
    "sensorState" : [ 
        {
            "data" : "377",
            "sensor" : "solar",
            "time" : ISODate("2017-09-20T19:42:58.766Z")
        }, 
        {
            "data" : "35",
            "sensor" : "photosynthetic",
            "time" : ISODate("2017-09-20T19:42:58.782Z")
        }, 
        {
            "data" : "370",
            "sensor" : "solar",
            "time" : ISODate("2017-09-20T19:43:29.089Z")
        }, 
        {
            "data" : "400",
            "sensor" : "solar",
            "time" : ISODate("2017-09-20T19:44:29.089Z")
        }, 
        {
            "data" : "35",
            "sensor" : "photosynthetic",
            "time" : ISODate("2017-09-20T19:43:29.110Z")
        }
    ]
}

I want to retrieve only those sub documents in the sensorState array which match sensor=solar. So, I tried to do like this:

db.getCollection('sensorDB').find({ },
    { "sensorState" : { $elemMatch: {data : "35" } } })

But it shows only one result:

{
    "_id" : ObjectId("59b0ea9b3a91af574a3e0464"),
    "sensorState" : [ 
        {
            "data" : "35",
            "sensor" : "photosynthetic",
            "time" : ISODate("2017-09-20T19:42:58.782Z")
        }
    ]
}

However, I want to find all sub documents in the sensorState array which match sensor=solar. It looks like $elemMatch just can select one result.

2 个答案:

答案 0 :(得分:1)

您正尝试使用elemMatch进行投影但是根据the docs

  

$elemMatch运算符将查询结果中字段的内容限制为仅包含与$elemMatch条件匹配的第一个元素。

因此,您看到的行为是elemMatch-in-a-projection的预期行为。

您可以在汇总管道中使用$project$filter来获取最终结果。

鉴于您的OP中包含的示例文档,请执行以下命令...

db.getCollection('elemMatch').aggregate([
  // projects on the sensorState sub documents and filters the output to only return sub 
  // documents having data=35
  {$project: {
      sensorState: {
        $filter: {
            input: "$sensorState",
            as: "sensorState",
            cond: { $eq: [ "$$sensorState.data", '35' ] }
          }
        }
      }
  }
])

...将返回:

{
    "_id" : ObjectId("59b0ea9b3a91af574a3e0464"),
    "sensorState" : [ 
        {
            "data" : "35",
            "sensor" : "photosynthetic",
            "time" : ISODate("2017-09-20T19:42:58.782Z")
        }, 
        {
            "data" : "35",
            "sensor" : "photosynthetic",
            "time" : ISODate("2017-09-20T19:43:29.110Z")
        }
    ]
}

您的问题显示了data=35上的尝试匹配,但它也说明了:

  

我想找到所有sensorState.sensor = solar

所以,这是带有该条件的上述命令:

db.getCollection('elemMatch').aggregate([
  // projects on the sensorState sub documents and filters the output to only return sub 
  // documents having sensor=solar
  {$project: {
      sensorState: {
        $filter: {
            input: "$sensorState",
            as: "sensorState",
            cond: { $eq: [ "$$sensorState.sensor", 'solar' ] }
          }
        }
      }
  }
])

返回:

{
    "_id" : ObjectId("59b0ea9b3a91af574a3e0464"),
    "sensorState" : [ 
        {
            "data" : "377",
            "sensor" : "solar",
            "time" : ISODate("2017-09-20T19:42:58.766Z")
        }, 
        {
            "data" : "370",
            "sensor" : "solar",
            "time" : ISODate("2017-09-20T19:43:29.089Z")
        }, 
        {
            "data" : "400",
            "sensor" : "solar",
            "time" : ISODate("2017-09-20T19:44:29.089Z")
        }
    ]
}

答案 1 :(得分:1)

我自己找到了答案。使用聚合 答案是这样的

db.getCollection('sensorDB').aggregate(
        {$match: {machineID:"b83c"}},
        {$unwind:"$sensorState"},
        {$match: {"sensorState.sensor":"solar"}}
)