我想获得MongoDB集合中所有键的名称。
例如,从这个:
db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { egg : ['cat'] } );
db.things.insert( { type : [] } );
db.things.insert( { hello : [] } );
我想获得唯一的键:
type, egg, hello
我想获得MongoDB集合中所有键的名称。
例如,从这个:
db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { egg : ['cat'] } );
db.things.insert( { type : [] } );
db.things.insert( { hello : [] } );
我想获得唯一的键:
type, egg, hello
当前回答
我认为这里提到的最好的方法是在mongod 3.4.4+中,但不使用$unwind操作符,只使用管道中的两个阶段。相反,我们可以使用$mergeObjects和$objectToArray操作符。
在$group阶段,我们使用$mergeObjects操作符返回一个文档,其中键/值来自集合中的所有文档。
然后是$project,我们使用$map和$objectToArray返回键。
let allTopLevelKeys = [
{
"$group": {
"_id": null,
"array": {
"$mergeObjects": "$$ROOT"
}
}
},
{
"$project": {
"keys": {
"$map": {
"input": { "$objectToArray": "$array" },
"in": "$$this.k"
}
}
}
}
];
现在,如果我们有一个嵌套的文档,想要获得键,这是可行的。为了简单起见,让我们考虑一个包含简单嵌入式文档的文档,它看起来像这样:
{field1: {field2: "abc"}, field3: "def"}
{field1: {field3: "abc"}, field4: "def"}
下面的管道会生成所有的键(field1, field2, field3, field4)。
let allFistSecondLevelKeys = [
{
"$group": {
"_id": null,
"array": {
"$mergeObjects": "$$ROOT"
}
}
},
{
"$project": {
"keys": {
"$setUnion": [
{
"$map": {
"input": {
"$reduce": {
"input": {
"$map": {
"input": {
"$objectToArray": "$array"
},
"in": {
"$cond": [
{
"$eq": [
{
"$type": "$$this.v"
},
"object"
]
},
{
"$objectToArray": "$$this.v"
},
[
"$$this"
]
]
}
}
},
"initialValue": [
],
"in": {
"$concatArrays": [
"$$this",
"$$value"
]
}
}
},
"in": "$$this.k"
}
}
]
}
}
}
]
只需稍加努力,我们就可以获得数组字段中所有子文档的键,其中元素也是object。
其他回答
下面是用Python编写的示例: 这个示例内联返回结果。
from pymongo import MongoClient
from bson.code import Code
mapper = Code("""
function() {
for (var key in this) { emit(key, null); }
}
""")
reducer = Code("""
function(key, stuff) { return null; }
""")
distinctThingFields = db.things.map_reduce(mapper, reducer
, out = {'inline' : 1}
, full_response = True)
## do something with distinctThingFields['results']
我认为这里提到的最好的方法是在mongod 3.4.4+中,但不使用$unwind操作符,只使用管道中的两个阶段。相反,我们可以使用$mergeObjects和$objectToArray操作符。
在$group阶段,我们使用$mergeObjects操作符返回一个文档,其中键/值来自集合中的所有文档。
然后是$project,我们使用$map和$objectToArray返回键。
let allTopLevelKeys = [
{
"$group": {
"_id": null,
"array": {
"$mergeObjects": "$$ROOT"
}
}
},
{
"$project": {
"keys": {
"$map": {
"input": { "$objectToArray": "$array" },
"in": "$$this.k"
}
}
}
}
];
现在,如果我们有一个嵌套的文档,想要获得键,这是可行的。为了简单起见,让我们考虑一个包含简单嵌入式文档的文档,它看起来像这样:
{field1: {field2: "abc"}, field3: "def"}
{field1: {field3: "abc"}, field4: "def"}
下面的管道会生成所有的键(field1, field2, field3, field4)。
let allFistSecondLevelKeys = [
{
"$group": {
"_id": null,
"array": {
"$mergeObjects": "$$ROOT"
}
}
},
{
"$project": {
"keys": {
"$setUnion": [
{
"$map": {
"input": {
"$reduce": {
"input": {
"$map": {
"input": {
"$objectToArray": "$array"
},
"in": {
"$cond": [
{
"$eq": [
{
"$type": "$$this.v"
},
"object"
]
},
{
"$objectToArray": "$$this.v"
},
[
"$$this"
]
]
}
}
},
"initialValue": [
],
"in": {
"$concatArrays": [
"$$this",
"$$value"
]
}
}
},
"in": "$$this.k"
}
}
]
}
}
}
]
只需稍加努力,我们就可以获得数组字段中所有子文档的键,其中元素也是object。
你可以用MapReduce来做:
mr = db.runCommand({
"mapreduce" : "my_collection",
"map" : function() {
for (var key in this) { emit(key, null); }
},
"reduce" : function(key, stuff) { return null; },
"out": "my_collection" + "_keys"
})
然后在结果集合上单独运行,以便找到所有的键:
db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]
沿着@James Cropcho的回答,我找到了下面这个我觉得超级好用的方法。这是一个二进制工具,这正是我正在寻找的: mongoeye。
使用这个工具,大约花了2分钟从命令行导出我的模式。
如果你正在使用mongodb 3.4.4及以上版本,那么你可以使用$objectToArray和$group聚合来使用下面的聚合
db.collection.aggregate([
{ "$project": {
"data": { "$objectToArray": "$$ROOT" }
}},
{ "$project": { "data": "$data.k" }},
{ "$unwind": "$data" },
{ "$group": {
"_id": null,
"keys": { "$addToSet": "$data" }
}}
])
下面是工作示例