You would want to be working on a negative case of the $all
operator combined with $not
:
db.collection.find({
"images.thumbnails.size": {
"$not": { "$all": [ 150, 320, 800 ] }
}
})
Possibly in versions prior to MongoDB 2.6 you might need to adapt this due to a change in the logic for $all
but the same thing is constructed using $and
:
db.collection.find({
"$and": [
{ "$ne": { "images.thumbnail.size": 150 } },
{ "$ne": { "images.thumbnail.size": 320 } },
{ "$ne": { "images.thumbnail.size": 800 } }
]
})
Noting of course that these statements are matching "documents" and not the elements of your "images" array, to actually filter those you would need to apply this to aggregate:
db.collection.aggregate([
// Match the documents meeting the conditions
{ "$match": {
"images.thumbnails.size": {
"$not": { "$all": [ 150, 320, 800 ] }
}
}},
// Unwind the images array
{ "$unwind": "$images" },
// Filter out any array elements that do not match
{ "$match": {
"images.thumbnails.size": {
"$not": { "$all": [ 150, 320, 800 ] }
}
}},
// Optional: Projection re-shaping
{ "$project": {
"_id": {
"_id": "$_id",
"images": {
"name": "$images.name",
"width": "$images.width",
"height": "$images.height"
}
},
"thumbs": "$images.thumbnails"
}},
// Optional: unwind the thumbnails
{ "$unwind": "$thumbs" },
// Optional: group back only the sizes
{ "$group": {
"_id": "$_id",
"thumbs": { "$push": "$thumbs.size" }
}},
// Optional: Project with the difference on the set
{ "$project": {
"_id": "$_id._id",
"images": {
"name": "$_id.images.name",
"width": "$_id.images.width",
"height": "$_id.images.height",
"missingThumbs": { "$setDifference": [
[ 150, 320, 800 ],
"$thumbs"
]}
}
}},
// Restore the images array
{ "$group": {
"_id": "$_id",
"images": { "$push": "$images" }
}}
])
So this uses $setDifference
to take this a little further and tell you which of the "thumbnail sizes" you were testing for did not exist. The stage is optional as that operator is only available from MongoDB 2.6 and upwards, so otherwise just remove the stages marked as "Optional:" to allow this to filter the "images" array entries.
You also could do the "difference" matching in versions prior to 2.6 but it is a fair bit more involved, but you may want to try and work that part out.
As for your full generation edit here would be the full listing:
db.collection.aggregate([
// Match the documents meeting the conditions
{ "$match": {
"images.thumbnails.size": {
"$not": { "$all": [ 150, 320, 800 ] }
}
}},
// Unwind the images array
{ "$unwind": "$images" },
// Filter out any array elements that do not match
{ "$match": {
"images.thumbnails.size": {
"$not": { "$all": [ 150, 320, 800 ] }
}
}},
// Projection re-shaping
{ "$project": {
"_id": {
"_id": "$_id",
"images": {
"name": "$images.name",
"width": "$images.width",
"height": "$images.height"
}
},
"thumbs": "$images.thumbnails"
}},
// unwind the thumbnails
{ "$unwind": "$thumbs" },
// group back only the sizes
{ "$group": {
"_id": "$_id",
"thumbs": { "$push": "$thumbs.size" }
}},
// Project missingThumbs
{ "$project": {
"missingThumbs": { "$setDifference": [
[ 150, 320, 800 ],
"$thumbs"
]}
}},
// Unwind the missing thumbs
{ "$unwind": "$missingThumbs" },
// Project a size test
{ "$project": {
"missingThumbs": 1,
"larger": { "$gte": [
"$_id.images.width",
"$missingThumbs"
]}
}},
// Match the size test
{ "$match": { "larger": true }},
// Group back the missing thumbs
{ "$group": {
"_id": "$_id",
"missingThumbs": { "$push": "$missingThumbs" }
}},
// Project the images entry
{ "$project": {
"_id": "$_id._id",
"images": {
"name": "$_id.images.name",
"width": "$_id.images.width",
"height": "$_id.images.height",
"missingThumbs": "$missingThumbs"
}
}},
// Restore the images array
{ "$group": {
"_id": "$_id",
"images": { "$push": "$images" }
}}
])
Nothing optional in here as you would clearly be using the features to detect the thumbnails you don't already have. The additional steps are there to compare the size of the missing thumb against the size of the image. Anything not detected as "larger" would be excluded.