Bạn có thể làm điều đó bằng cách sử dụng mapReduce :
Để chỉ lấy tên trường ở cấp cơ sở:
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 }});
Điều này sẽ xuất ra một cái gì đó như thế này:
{
"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
}
Để nhận tên trường và bất kỳ hoặc tất cả (toàn bộ tài liệu) giá trị của nó:
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 }});
Điều này sẽ xuất ra một cái gì đó như thế này:
{
"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
}
Để lấy tên trường trong tài liệu con (không có đường dẫn):
Để làm điều đó, bạn sẽ phải sử dụng cửa hàng 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])
});
}});
Và thay đổi bản đồ của bạn thành:
function () {
getFields(this);
}
Bây giờ hãy chạy db.loadServerScripts()
để tải chúng.
Để lấy tên trường trong các tài liệu con (có đường dẫn):
Phiên bản trước sẽ chỉ trả về tên trường, không phải toàn bộ đường dẫn để lấy chúng, mà bạn sẽ cần nếu điều bạn muốn làm là đổi tên các khóa đó. Để lấy đường dẫn:
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 + '.')
});
}});
Và thay đổi bản đồ của bạn thành:
function () {
getFields(this, '');
}
Để loại trừ các kết hợp đường dẫn trùng lặp:
Lưu ý rằng nếu bạn có trường fkfoo.fkbar
, nó sẽ trả về fkfoo
và fkfoo.fkbar
. Nếu bạn không muốn các kết quả trùng khớp về đường dẫn, thì:
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);
});
}});
Quay lại câu hỏi của bạn, đổi tên các trường đó:
Với tùy chọn cuối cùng này, bạn nhận được tất cả các đường dẫn bao gồm các khóa bắt đầu bằng fk
, vì vậy bạn có thể sử dụng $rename
cho điều đó.
Tuy nhiên, $rename
không hoạt động đối với những người chứa mảng, vì vậy đối với những người bạn có thể sử dụng forEach
để cập nhật. Xem MongoDB đổi tên trường cơ sở dữ liệu trong mảng
Lưu ý về hiệu suất:
MapReduce không phải là suy nghĩ đặc biệt nhanh, vì vậy bạn có thể muốn chỉ định { out: "fk_fields"}
để xuất kết quả vào một bộ sưu tập mới có tên là fk_fields
và truy vấn những kết quả đó sau đó, nhưng điều đó sẽ phụ thuộc vào trường hợp sử dụng của bạn.
Các tối ưu có thể có cho các trường hợp cụ thể (giản đồ nhất quán):
Ngoài ra, lưu ý rằng nếu bạn biết rằng lược đồ của tài liệu của bạn luôn giống nhau, thì bạn chỉ cần kiểm tra một trong số chúng để lấy các trường của nó, vì vậy bạn có thể thực hiện việc đó bằng cách thêm limit: 1
vào đối tượng tùy chọn hoặc chỉ truy xuất một tài liệu với findOne
và đọc các trường của nó trong cấp ứng dụng.