问题
I have a Mongo DB with the following object:
[
{
"link" : "xxxxx.jpg"
"_id" : ObjectId("5501b1648ef0b4eccc41814e"),
"processed" : {
"320" : true,
"480" : true,
"540" : true,
"720" : true,
"800" : true,
"1080" : true,
"original" : false,
"iPhone" : true
}
}
]
I am trying to query where any of the processed values is false, but I cannot seem to figure out how to query where I do not know which key matches. Is this possible without looping through all documents?
回答1:
With a document schema like you've put in the original post, we can use Javascript to;
- Find all records
- Loop through
processed
object - If any value equates to boolean
false
, add the_id
to an array - Print the
_id
's that have afalse
value inprocessed
object
Query to run
var arrDoc = [];
db.test.find().forEach(
function anyFalse(doc) {
for(key in doc.processed) {
if(doc.processed.hasOwnProperty(key) && doc.processed[key] === false) {
arrDoc.push(doc._id);
break;
}
}
});
print( arrDoc.join("\r\n") );
Example document
{
"_id" : ObjectId("5107de525ed6103609000016"),
"link" : "xxxxx.jpg",
"processed" : {
"320" : true,
"480" : true,
"540" : true,
"720" : true,
"800" : true,
"1080" : true,
"original" : false,
"iPhone" : true
}
}
Example output
ObjectId("5107de525ed6103609000016")
Further notes
You can store this javascript function anyFalse
into Mongo and call it when you need it. See store javascript function on server
Edit
As noted in the comments, you have an images
array. This function will loop through all the images
array to check child processed
for false.
Query to run
var arrDoc = [];
db.test.find().forEach(
function anyFalse(doc) {
var len = doc.images.length;
for( var i = 0; i < len; i++ ) {
for(key in doc.images[i].processed) {
if(doc.images[i].processed.hasOwnProperty(key) && doc.images[i].processed[key] === false) {
arrDoc.push(doc.images[i]._id);
break;
}
}
}
});
print( arrDoc.join("\r\n") );
Example document
{
"_id" : ObjectId("5534fe2f3614af9afd23310a"),
"images" : [
{
"_id" : ObjectId("5107de525ed6103609000016"),
"link" : "xxxxx.jpg",
"processed" : {
"320" : true,
"480" : true,
"540" : true,
"720" : true,
"800" : true,
"1080" : true,
"original" : true,
"iPhone" : true
}
},
{
"_id" : ObjectId("5107de525ed6103609000017"),
"link" : "xxxxx.jpg",
"processed" : {
"320" : true,
"480" : true,
"540" : true,
"720" : true,
"800" : true,
"1080" : true,
"original" : false,
"iPhone" : true
}
}
]
}
回答2:
With MongoDB 3.4.4, use the aggregation framework to query the document. This is made possible with the $objectToArray operator which allows you to map the keys in the processed field to an array of key/value pairs. The list will be easy to filter and get the key(s) that matches whatever condition you have.
In the following example, the aggregation pipeline consists of an extra field which holds the key(s) that match the above condition of having a false value, so ideally it will be an array:
db.collection.aggregate([
{ "$addFields": {
"notProcessed": {
"$map" : {
"input": {
"$filter": {
"input": { "$objectToArray": "$processed" },
"as": "el",
"cond": { "$not": "$$el.v" }
}
},
"in": "$$this.k"
}
}
} }
])
which yields
{
"_id" : ObjectId("5501b1648ef0b4eccc41814e"),
"link" : "xxxxx.jpg",
"processed" : {
"320" : true,
"480" : true,
"540" : true,
"720" : true,
"800" : true,
"1080" : true,
"original" : false,
"iPhone" : true
},
"notProcessed" : [
"original"
]
}
Explanations
Starting with the nested expression
{
"$filter": {
"input": { "$objectToArray": "$processed" },
"as": "el",
"cond": { "$not": "$$el.v" }
}
}
the input to $filter operator { "$objectToArray": "$processed" } will convert the keys in the processed
key to this array:
[
{
"k" : "320",
"v" : true
},
{
"k" : "480",
"v" : true
},
{
"k" : "540",
"v" : true
},
{
"k" : "720",
"v" : true
},
{
"k" : "800",
"v" : true
},
{
"k" : "1080",
"v" : true
},
{
"k" : "original",
"v" : false
},
{
"k" : "iPhone",
"v" : true
}
]
and $filter will filter the above array to have only the object elements whose v
property is NOT
true
:
[
{
"k" : "original",
"v" : false
}
]
$map will then return a mapped array with just the values
[ { "k" : "original", "v" : false } ] => [ "original" ]
so you end up with just
[ "original" ]
as a result.
With older MongoDB versions, it's going to be pretty difficult to issue queries against dynamic keys. Consider modifying your schema to follow this document model which is easier to query:
// this operation changes the schema
var processed = [];
db.collection.find().forEach( function(doc) {
for(key in doc.processed) {
if(doc.processed.hasOwnProperty(key)) {
var item = { key: key, value: doc.processed[key] }
processed.push(item);
}
}
doc.processed = processed;
db.collection.save(doc);
});
// modified schema
{
"link": "xxxxx.jpg"
"_id": ObjectId("5501b1648ef0b4eccc41814e"),
"processed": [
{ "key": "320", "value": true },
{ "key": "480", "value": true },
{ "key": "540", "value": true },
{ "key": "720", "value": true },
{ "key": "800", "value": true },
{ "key": "1080", "value": true },
{ "key": "original", "value": false },
{ "key": "iPhone", "value": true }
]
}
Your find query will be simply
db.collection.find({"processed.value": false});
or use $map and $filter to return the keys with false
values as
db.collection.aggregate([
{ "$project": {
"link": 1,
"notProcessed": {
"$map" : {
"input": {
"$filter": {
"input": "$processed",
"as": "el",
"cond": { "$not": "$$el.v" }
}
},
"in": "$$this.k"
}
}
} }
])
来源:https://stackoverflow.com/questions/29745878/mongo-match-where-object-key-is-variable