U kunt dat doen met mapReduce :
Om alleen de veldnamen op hoofdniveau te krijgen:
db.collection.mapReduce(function () {
Object.keys(this).map(function(key) {
if (key.match(/^fk/)) emit(key, null);
// OR: key.indexOf("fk") === 0
});
}, function(/* key, values */) {
// No need for params or to return anything in the
// reduce, just pass an empty function.
}, { out: { inline: 1 }});
Dit zal zoiets als dit opleveren:
{
"results": [{
"_id": "fkKey1",
"value": null
}, {
"_id": "fkKey2",
"value": null
}, {
"_id": "fkKey3",
"value": null
}],
"timeMillis": W,
"counts": {
"input": X,
"emit": Y,
"reduce": Z,
"output": 3
},
"ok" : 1
}
Om veldnamen en een of alle (hele document) waarden te krijgen:
db.test.mapReduce(function () {
var obj = this;
Object.keys(this).map(function(key) {
// With `obj[key]` you will get the value of the field as well.
// You can change `obj[key]` for:
// - `obj` to return the whole document.
// - `obj._id` (or any other field) to return its value.
if (key.match(/^fk/)) emit(key, obj[key]);
});
}, function(key, values) {
// We can't return values or an array directly yet:
return { values: values };
}, { out: { inline: 1 }});
Dit zal zoiets als dit opleveren:
{
"results": [{
"_id": "fkKey1",
"value": {
"values": [1, 4, 6]
}
}, {
"_id": "fkKey2",
"value": {
"values": ["foo", "bar"]
}
}],
"timeMillis": W,
"counts": {
"input": X,
"emit": Y,
"reduce": Z,
"output": 2
},
"ok" : 1
}
Om veldnamen in subdocumenten te krijgen (zonder pad):
Om dat te doen, moet je store JavaScript functions on the Server
:
db.system.js.save({ _id: "hasChildren", value: function(obj) {
return typeof obj === "object";
}});
db.system.js.save({ _id: "getFields", value: function(doc) {
Object.keys(doc).map(function(key) {
if (key.match(/^fk/)) emit(key, null);
if (hasChildren(doc[key])) getFields(doc[key])
});
}});
En verander je kaart in:
function () {
getFields(this);
}
Voer nu db.loadServerScripts()
uit om ze te laden.
Om veldnamen in subdocumenten te krijgen (met pad):
De vorige versie retourneert alleen veldnamen, niet het hele pad om ze te krijgen, wat je nodig hebt als je die sleutels wilt hernoemen. Om het pad te krijgen:
db.system.js.save({ _id: "getFields", value: function(doc, prefix) {
Object.keys(doc).map(function(key) {
if (key.match(/^fk/)) emit(prefix + key, null);
if (hasChildren(doc[key]))
getFields(doc[key], prefix + key + '.')
});
}});
En verander je kaart in:
function () {
getFields(this, '');
}
Overlappende padovereenkomsten uitsluiten:
Merk op dat als je een veld fkfoo.fkbar
. hebt , zal het fkfoo
. teruggeven en fkfoo.fkbar
. Als u geen overlappende padovereenkomsten wilt, dan:
db.system.js.save({ _id: "getFields", value: function(doc, prefix) {
Object.keys(doc).map(function(key) {
if (hasChildren(doc[key]))
getFields(doc[key], prefix + key + '.')
else if (key.match(/^fk/)) emit(prefix + key, null);
});
}});
Teruggaand op uw vraag, hernoemen van die velden:
Met deze laatste optie krijg je alle paden die sleutels bevatten die beginnen met fk
, zodat u $rename
kunt gebruiken
daarvoor.
Echter, $rename
werkt niet voor degenen die arrays bevatten, dus voor degenen die u zou kunnen gebruiken forEach
om de update uit te voeren. Zie MongoDB hernoem databaseveld binnen array
Prestatienotitie:
MapReduce is niet bijzonder snel bedacht, dus misschien wilt u { out: "fk_fields"}
specificeren om de resultaten uit te voeren in een nieuwe verzameling genaamd fk_fields
en vraag die resultaten later op, maar dat hangt af van uw gebruik.
Mogelijke optimalisaties voor specifieke gevallen (consistent schema):
Houd er ook rekening mee dat als u weet dat het schema van uw documenten altijd hetzelfde is, u slechts één van hen hoeft aan te vinken om de velden te krijgen, zodat u dat kunt doen door limit: 1
toe te voegen. naar het options-object of gewoon één document ophalen met findOne
en het lezen van de velden op applicatieniveau.