Suppose you have a map of variable size in mongodb. How can you get documents that have only a certain map size, for example >2?
{map:{k1:v1, k2:v2, k3,v3}}
{map:{k1:v1, k2:v2}}
With MongoDB 3.6 and greater:
Use the $expr operator in your query, this allows you to use aggregation framework operators in particular the $objectToArray operator that converts the map subdocument to an array of key values i.e.
{ map: {
k1:v1,
k2:v2,
k3,v3
}}
is transformed to the array
{ map: [
{ k: 'k1', v: 'v1' },
{ k: 'k2', v: 'v2' },
{ k: 'k3', v: 'v3' }
]}
With the array you can then use $size to get the length and compare using the comparison query operator $gt.
An example follows which shows the complete query:
db.test.find({
"$expr": {
"$gt": [
{ "$size": { "$objectToArray": "$map" } },
2
]
}
})
For MongoDB versions which do not have support for the above operators, you'd need to pre-calculate such map size if you want to do queries on them later so consider creating a new field "keyCount" which holds the number of keys for the map subdocument. Consider the following demonstration:
Populate test collection
db.test.insert([
{
"_id" : 1,
"map" : {
"k1" : "v1",
"k2" : "v2",
"k3" : "v3"
}
},
{
"_id" : 2,
"map" : {
"k1" : "v1",
"k2" : "v2"
}
}
]);
As with the current design, you would need a mechanism to get all count of the keys inside the map document. This is possible through Map-Reduce. The following mapreduce operation will populate a separate collection with the new field "keyCount" added:
var mr = db.runCommand({
"mapreduce": "test",
"map" : function() {
var obj = this;
obj['keyCount'] = Object.keys(this.map).length;
emit(this._id, obj);
},
"reduce" : function(key, stuff) { return null; },
"out": "my_collection" + "_keys"
})
To get documents that have only a certain map size, for example > 2, run the query on the resulting collection:
db[mr.result].find({ "value.keyCount": { "$gt": 2 } });
Map Reduce Output
/* 0 */
{
"result" : "my_collection_keys",
"timeMillis" : 7,
"counts" : {
"input" : 2,
"emit" : 2,
"reduce" : 0,
"output" : 2
},
"ok" : 1
}
Query Output
/* 0 */
{
"_id" : 1,
"value" : {
"_id" : 1,
"map" : {
"k1" : "v1",
"k2" : "v2",
"k3" : "v3"
},
"keyCount" : 3
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With