Reputation: 782
This is an example document in my MongoDB collection:
var user: [{
"_id" : ObjectId("5391b11a3c0a9ac01300006d"),
"injurydata" : {
"injuryinformation" : [
{
"status" : "current",
"updateddate" : ISODate("2014-06-06T12:16:20.306Z"),
"updatedby" : "",
"dateofinjury" : ISODate("2014-06-27T18:30:00.000Z"),
"_id" : ObjectId("5391b11a3c0a9ac01300006f")
}
]
}
},
{
"_id" : ObjectId("5391b11a3c0a9ac01300006d"),
"injurydata" : {
"injuryinformation" : [
{
"status" : "current",
"updateddate" : ISODate("2014-06-06T12:16:20.306Z"),
"dateofinjury" : ISODate("2014-06-28T18:30:00.000Z"),
"_id" : ObjectId("5391b11a3c0a9ac01300006f")
}
]
}
},
{
"_id" : ObjectId("5391b11a3c0a9ac01300006d"),
"injurydata" : {
"injuryinformation" : [
{
"status" : "current",
"dateofinjury" : ISODate("2014-08-10T18:30:00.000Z"),
"_id" : ObjectId("5391b11a3c0a9ac01300006f")
}
]
}
}]
From this document, I want only those injuryinformation
array elements for which dateofinJURY
is between 2014-06-28 and 2014-06-30, so I will only get first two elements.
I tried this query but I still get the whole array
db.user.find(
{
$and:
[
{"injury.injurydata.injuryinformation.dateofinjury":
{"$gte": ISODate("2014-05-21T08:00:00Z") , "$lt": ISODate("2014-06- 03T08:00:00Z")}},
{"_id":ObjectId("538d9a1dd173e5202a00005d")}
],
})
Upvotes: 2
Views: 3053
Reputation: 151190
Well the _id
match is fairly explicit in your statement so not only will that negate the need for the "range" match on array entries but it also negates any usage of $and
which is actually implicit in MongoDB queries. The $and
condition is essentially the "default" and is not required unless you are actually asking for multiple conditions on te same field. But here you are not.
Matching multiple array elements you need to use .aggregate()
as the "projection" available to .find()
cannot show you more than one matching entry from an array:
db.collection.aggregate([
// Match the document but not actually the array
{ "$match": {
"injury.injurydata.injuryinformation.dateofinjury": {
"$gte": ISODate("2014-05-21T08:00:00Z"),
"$lt": ISODate("2014-06-03T08:00:00Z")
},
"_id": ObjectId("538d9a1dd173e5202a00005d")
}},
// Unwind the arrays to "de-normalize" as documents
{ "$unwind": "$injury" },
{ "$unwind": "$injury.injurydata" },
{ "$unwind": "$injury.injurydata.injuryinformation" },
// Match the actual array elements
{ "$match": {
"injury.injurydata.injuryinformation.dateofinjury": {
"$gte": ISODate("2014-05-21T08:00:00Z"),
"$lt": ISODate("2014-06-03T08:00:00Z")
}
}},
// Group those back to and array, maybe?
{ "$group": {
"_id": "$_id",
"information": {
"$push": "$injury.injrydata.injuryinformation"
}
}}
])
Or "filtering" with $map
under MongoDB 2.6 and greater, looks longer but faster:
db.collection.aggregate([
// Match the document but not actually the array
{ "$match": {
"injury.injurydata.injuryinformation.dateofinjury": {
"$gte": ISODate("2014-05-21T08:00:00Z"),
"$lt": ISODate("2014-06-03T08:00:00Z")
},
"_id": ObjectId("538d9a1dd173e5202a00005d")
}},
// Unwind the arrays to "de-normalize" as documents
{ "$unwind": "$injury" },
{ "$unwind": "$injury.injurydata" },
// Project with the "$map" filter
{ "$project": {
"information": {
"$setDifference": [
"$map": {
"input": "$injury.injurydata.injuryinformation",
"as": "el",
"in": {
"$cond": [
{
"$and": [
{
"$gte": [
"$$el.dateofinjury",
ISODate("2014-05-21T08:00:00Z")
]
},
{
"$lt": [
"$$el.dateofinjury",
ISODate("2014-06-03T08:00:00Z")
]
}
]
}
],
false
}
},
[false]
]
}
}}
])
The $and condition used in there is a different form that is available to aggregation framework expressions outside of $match
operations that are equivalent to .find()
queries.
Not sure where the "injury" top line comes from there as it is not represented in your sample, but I assume you actually have it.
Upvotes: 2