I want to find all key names from a collection that partially match a certain string.
The closest I got was to check if a certain key exists, but that's an exact match:
db.collection.find({ "fkClientID": { $exists:1 }})
I'd like to get all keys that start with fk
instead.
You can do that using mapReduce:
To get just the field names at root level:
This will output something like this:
To get field names and any or all (whole doc) its values:
This will output something like this:
To get field names in subdocuments (without path):
To do that you will have to use
store JavaScript functions on the Server
:And change your map to:
Now run
db.loadServerScripts()
to load them.To get field names in subdocuments (with path):
The previous version will just return field names, not the whole path to get them, which you will need if what you want to do is rename those keys. To get the path:
And change your map to:
To exclude overlapping path matches:
Note that if you have a field
fkfoo.fkbar
, it will returnfkfoo
andfkfoo.fkbar
. If you don't want overlapping path matches, then:Going back to your question, renaming those fields:
With this last option, you get all the paths that include keys that start with
fk
, so you can use$rename
for that.However,
$rename
doesn't work for those that contain arrays, so for those you could useforEach
to do the update. See MongoDB rename database field within arrayPerformance note:
MapReduce is not particularly fast thought, so you may want to specify
{ out: "fk_fields"}
to output the results into a new collection calledfk_fields
and query those results later, but that will depend on your use case.Possible optimisations for specific cases (consistent schema):
Also, note that if you know that the schema of your documents is always the same, then you just need to check one of them to get its fields, so you can do that adding
limit: 1
to the options object or just retrieving one document withfindOne
and reading its fields in the application level.If you have the latest MongoDB 3.4.4 then you can use
$objectToArray
in an aggregate statement with$redact
as the the most blazing fast way this can possibly be done with native operators. Not that scanning the collection is "fast". but as fast as you get for this:The presently undocumented
$objectToArray
translates an "object" into "key" and "value" form in an array. So this:Becomes this:
Used with
$$ROOT
which is a special variable referring to the current document "object", we translate to an array so the values of"k"
can be inspected.Then it's just a matter of applying
$filter
and using$substr
to get the preceding characters of the "key" string.For the record, this would be the MongoDB 3.4.4 optimal way of obtaining an unique list of the matching keys:
That's the safe provision, which is considering that the key may not be present in all documents and that there could possibly be multiple keys in the document.
If you are absolutely certain that you "always" have the key present in the document and that there will only be one, then you can shorten to just
$group
:The most efficient way in earlier versions would be using the
$where
syntax that allows a JavaScript expression to evaluate. Not that anything that evaluates JavaScript is the "most" efficient thing you can do, but analyzing "keys" as opposed to "data" is not optimal for any data store:The inline
function
there is just shell shorthand and this could also be written as:The only requirement for
$where
is that the expression returns atrue
value for any document you want to return, so the documents return unaltered.