Namho Kim
Namho Kim

Reputation: 53

Project on specific elements in a nested array

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.

Upvotes: 2

Views: 2346

Answers (2)

Namho Kim
Namho Kim

Reputation: 53

I found answer myself. use aggregate the answer is like this

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

Upvotes: 3

glytching
glytching

Reputation: 48015

You are attempting to project with elemMatch but according to the docs

The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.

So, the behaviour you are seeing is the expected behaviour for elemMatch-in-a-projection.

You can get use $project and $filter in an aggregation pipeline to get your end result.

Given the sample document included in your OP the following command ...

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' ] }
          }
        }
      }
  }
])

... will return:

{
    "_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")
        }
    ]
}

Your questions shows an attempted match on data=35 but it also states:

I want to find all sensorState.sensor = solar

So, here's the above command with that condition:

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' ] }
          }
        }
      }
  }
])

Which returns:

{
    "_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")
        }
    ]
}

Upvotes: 1

Related Questions