Mongo - Match where object key is variable

筅森魡賤 提交于 2019-12-24 04:49:08

问题


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 a false value in processed 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!