Можете да направите това с помощта на mapReduce :
За да получите само имената на полетата на основно ниво:
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 }});
Това ще изведе нещо подобно:
{
"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
}
За да получите имена на полета и някои или всички (целия документ) стойности:
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 }});
Това ще изведе нещо подобно:
{
"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
}
За да получите имена на полета в поддокументи (без път):
За да направите това, ще трябва да използвате 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])
});
}});
И променете картата си на:
function () {
getFields(this);
}
Сега стартирайте db.loadServerScripts()
за да ги заредите.
За да получите имена на полета в поддокументи (с път):
Предишната версия просто ще върне имена на полета, а не целия път, за да ги получите, което ще ви трябва, ако това, което искате да направите, е да преименувате тези ключове. За да получите пътя:
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 + '.')
});
}});
И променете картата си на:
function () {
getFields(this, '');
}
За да изключите съвпадения на припокриващи се пътища:
Имайте предвид, че ако имате поле fkfoo.fkbar
, ще върне fkfoo
и fkfoo.fkbar
. Ако не искате съвпадения на припокриващи се пътища, тогава:
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);
});
}});
Връщайки се към вашия въпрос, преименуване на тези полета:
С тази последна опция получавате всички пътища, които включват ключове, които започват с fk
, така че можете да използвате $rename
за това.
Въпреки това, $rename
не работи за тези, които съдържат масиви, така че за тези можете да използвате forEach
за да направите актуализацията. Вижте MongoDB поле за преименуване на база данни в масива
Бележка за ефективността:
MapReduce не се мисли особено бързо, така че може да искате да посочите { out: "fk_fields"}
за извеждане на резултатите в нова колекция, наречена fk_fields
и потърсете тези резултати по-късно, но това ще зависи от вашия случай на употреба.
Възможни оптимизации за конкретни случаи (последователна схема):
Освен това имайте предвид, че ако знаете, че схемата на вашите документи винаги е една и съща, тогава просто трябва да поставите отметка на един от тях, за да получите неговите полета, така че можете да направите това, като добавите limit: 1
към обекта с опции или просто извличане на един документ с findOne
и четене на неговите полета на ниво приложение.