Reputation: 12294
I have documents like this one at collection x
at MongoDB:
{
"_id" : ...
"attrKeys": [ "A1", "A2" ],
"attrs" : {
"A1" : {
"type" : "T1",
"value" : "13"
},
"A2" : {
"type" : "T2",
"value" : "14"
}
}
}
The A1
and A2
elements above are just examples: the attrs
field may hold any number of keys of any name. The key names in attrs
are stored in the attrNames
field.
I would like to query for documents which have an attr
with a sub-field of a given value. For example, a query for documents that have an element in the attr
key-map which sub-field type
is "T4". Something like this:
db.x.find({"attrs.$any.type": "T4"})
(The avobe is not legal MongoDB query language, but I think it could help to get the idea).
Is that query possible with MongoDB? Is there any workaround in the case MongoDB doesn't support that query? Thanks!
EDIT: original versions of the data model use an array for attrs
instead of a key-map. However, that changed in favour of a key-map in order to allow concurrent modifications on the same document.
I mean, using a key-map two independent clients can modify attrs
elements, as one client may do db.x.update({_id: "y"}, {$set: { "attrs.A1.value": "12" } }
and another client do db.x.update({_id: "y"}, {$set: { "attrs.A2.value": "55" } }
without interferring one each other.
In the case of using an array concurrent access is much more harder. Any hint on how it could be done?
Upvotes: 1
Views: 896
Reputation: 50406
This has always been possible with MongoDB because there has always been the ability to contruct query conditions using JavaScript evaluation:
db.attrs.find(function() {
var attrs = this.attrs;
return Object.keys(attrs).some(function(key) {
return attrs[key].value === "14"
});
})
Where that will correctly return the documents that match the condition here by searching the possible keys in the document for the required value.
But it's not really a question on "possible", but more one of "is this really a good idea", for which the basic answer is "No".
Databases are fickle beasts that do like to optimize with things like indexes and such, as well as their own expected operator set to make searching as efficient as possible to use. So yes, you can go through a language interpreter that effectively brute force evaluates a coded condition across each document, or you can reconsider your design pattern.
Databases love "order", so give it some, as there is a simple organized re-structure to the data you propose:
{
"attrs" : [
{ "key": "A1", "type" : "T1", "value" : "13" },
{ "key": "A2", "type" : "T2", "value" : "14" }
]
}
Organized that way the query becomes as simple as:
db.attrs.find({ "attrs.value": "14" })
And can of course support and use an index on any of those properties of the subdocument in the array.
MongoDB is afterall a "database", and like all databases it is most concerned with the "values" of it's properties rather that searching using the names of it's "keys". So things that represent meaningful "data" should not be part of the name of a "key", but rather they should be the "value" of a "key" as an "idendtifier", as demonstrate above.
Have a consistent path to the data you wish to query on is the optimal way to work with data inside MongoDB. Using a structure where key names are contantly changing, cannot be traversed by anything other than running code, and that is so much slower and worse for performance than using the native operations and facilites such as indexes.
Upvotes: 1