MongoDBコレクション内のすべてのキーの名前を取得したいのですが。
たとえば、これから:
db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { Egg : ['cat'] } );
db.things.insert( { type : [] } );
db.things.insert( { hello : [] } );
一意のキーを取得したいのですが。
type, Egg, hello
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"
})
それから、すべてのキーを見つけるために、結果として得られたコレクションに対してdistinctを実行します。
db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]
クリスティーナの答え をインスピレーションとして、私はVarietyというオープンソースのツールを作成しました。これはまさにこれです。 https:// github.com/variety/variety
$objectToArrray
のnew 3.4.4
バージョンで集約を使用して、すべてのトップキーと値のペアをドキュメント配列に変換し、その後に $unwind
& コレクション全体で異なるキーを取得するには、$group
with $addToSet
を指定します。
トップレベルの文書を参照するための $$ROOT
。
db.things.aggregate([
{"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
{"$unwind":"$arrayofkeyvalue"},
{"$group":{"_id":null,"allkeys":{"$addToSet":"$arrayofkeyvalue.k"}}}
])
単一の文書でキーを取得するには、以下のクエリを使用できます。
db.things.aggregate([
{"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
{"$project":{"keys":"$arrayofkeyvalue.k"}}
])
これを試して:
doc=db.thinks.findOne();
for (key in doc) print(key);
ターゲットコレクションがそれほど大きくない場合は、mongo Shellクライアントで試してみてください。
var allKeys = {};
db.YOURCOLLECTION.find().forEach(function(doc){Object.keys(doc).forEach(function(key){allKeys[key]=1})});
allKeys;
Pythonを使うコレクション内のすべての最上位キーのセットを返します。
#Using pymongo and connection named 'db'
reduce(
lambda all_keys, rec_keys: all_keys | set(rec_keys),
map(lambda d: d.keys(), db.things.find()),
set()
)
これが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']
Pymongoを使用してクリーンアップして再利用可能なソリューション:
from pymongo import MongoClient
from bson import Code
def get_keys(db, collection):
client = MongoClient()
db = client[db]
map = Code("function() { for (var key in this) { emit(key, null); } }")
reduce = Code("function(key, stuff) { return null; }")
result = db[collection].map_reduce(map, reduce, "myresults")
return result.distinct('_id')
使用法:
get_keys('dbname', 'collection')
>> ['key1', 'key2', ... ]
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" }
}}
])
これが実用的な 例 です
これは私にとってはうまくいきます:
var arrayOfFieldNames = [];
var items = db.NAMECOLLECTION.find();
while(items.hasNext()) {
var item = items.next();
for(var index in item) {
arrayOfFieldNames[index] = index;
}
}
for (var index in arrayOfFieldNames) {
print(index);
}
$unwind
演算子を使用せず、パイプラインで2つのステージのみを使用することなく、ここで説明した こちら の最善の方法はmongod 3.4.4+にあります。代わりに、 $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"
}
}
]
}
}
}
]
少しの努力で、要素がオブジェクトである配列フィールド内のすべてのサブ文書のキーを取得できます。
驚いたことに、重複した値を自動的にフィルタリングするために単純なjavascript
およびSet
ロジックを使用しても答えがありません。mongo Shellの単純な例を次に示します。
var allKeys = new Set()
db.collectionName.find().forEach( function (o) {for (key in o ) allKeys.add(key)})
for(let key of allKeys) print(key)
これにより、コレクション名に含まれるすべての一意のキーが印刷されます。collectionName。
すべてのキーから_id
を引いたリストを取得するには、次の集約パイプラインを実行することを検討してください。
var keys = db.collection.aggregate([
{ "$project": {
"hashmaps": { "$objectToArray": "$$ROOT" }
} },
{ "$project": {
"fields": "$hashmaps.k"
} },
{ "$group": {
"_id": null,
"fields": { "$addToSet": "$fields" }
} },
{ "$project": {
"keys": {
"$setDifference": [
{
"$reduce": {
"input": "$fields",
"initialValue": [],
"in": { "$setUnion" : ["$$value", "$$this"] }
}
},
["_id"]
]
}
}
}
]).toArray()[0]["keys"];
多少話題から外れるかもしれませんが、オブジェクトのすべてのキー/フィールドを再帰的にきれいに印刷することができます。
function _printFields(item, level) {
if ((typeof item) != "object") {
return
}
for (var index in item) {
print(" ".repeat(level * 4) + index)
if ((typeof item[index]) == "object") {
_printFields(item[index], level + 1)
}
}
}
function printFields(item) {
_printFields(item, 0)
}
コレクション内のすべてのオブジェクトが同じ構造を持つ場合に役立ちます。
Mongoldb ドキュメント に従って、distinct
の組み合わせ
単一のコレクションまたはビューにまたがって指定されたフィールドの固有値を見つけ、その結果を配列として返します。
そして インデックス コレクション操作は、与えられたキーまたはインデックスに対して可能なすべての値を返します。
コレクションの既存のインデックスを識別して記述するドキュメントのリストを保持する配列を返します
したがって、与えられたメソッドでは、登録されているすべてのインデックスについてコレクションをクエリして返すために、次のようなメソッドを使うことができます。明らかに他の任意の非同期アプローチを使用することができます。
async function GetFor(collection, index) {
let currentIndexes;
let indexNames = [];
let final = {};
let vals = [];
try {
currentIndexes = await collection.indexes();
await ParseIndexes();
//Check if a specific index was queried, otherwise, iterate for all existing indexes
if (index && typeof index === "string") return await ParseFor(index, indexNames);
await ParseDoc(indexNames);
await Promise.all(vals);
return final;
} catch (e) {
throw e;
}
function ParseIndexes() {
return new Promise(function (result) {
let err;
for (let ind in currentIndexes) {
let index = currentIndexes[ind];
if (!index) {
err = "No Key For Index "+index; break;
}
let Name = Object.keys(index.key);
if (Name.length === 0) {
err = "No Name For Index"; break;
}
indexNames.Push(Name[0]);
}
return result(err ? Promise.reject(err) : Promise.resolve());
})
}
async function ParseFor(index, inDoc) {
if (inDoc.indexOf(index) === -1) throw "No Such Index In Collection";
try {
await DistinctFor(index);
return final;
} catch (e) {
throw e
}
}
function ParseDoc(doc) {
return new Promise(function (result) {
let err;
for (let index in doc) {
let key = doc[index];
if (!key) {
err = "No Key For Index "+index; break;
}
vals.Push(new Promise(function (pushed) {
DistinctFor(key)
.then(pushed)
.catch(function (err) {
return pushed(Promise.resolve());
})
}))
}
return result(err ? Promise.reject(err) : Promise.resolve());
})
}
async function DistinctFor(key) {
if (!key) throw "Key Is Undefined";
try {
final[key] = await collection.distinct(key);
} catch (e) {
final[key] = 'failed';
throw e;
}
}
}
そのため、基本的な_id
インデックスでコレクションをクエリすると、次のようになります(テストコレクションには、テスト時に1つのドキュメントしかありません)。
Mongo.MongoClient.connect(url, function (err, client) {
assert.equal(null, err);
let collection = client.db('my db').collection('the targeted collection');
GetFor(collection, '_id')
.then(function () {
//returns
// { _id: [ 5ae901e77e322342de1fb701 ] }
})
.catch(function (err) {
//manage your error..
})
});
覚えておいて、これはNodeJSドライバに固有のメソッドを使用します。他のいくつかの答えが示唆しているように、集約フレームワークなどの他のアプローチがあります。あなたが簡単に結果を返す方法を作成して微調整できるので、私は個人的にこのアプローチがより柔軟であると思う。明らかに、これは最上位の属性のみを扱い、入れ子になった属性は扱いません。また、すべてのドキュメントが(メインの_id以外の)セカンダリインデックスがあるべきであることを保証するために、それらのインデックスはrequired
として設定されるべきです。
私はnodejsで書き込もうとしていて、ついにこれを思い付きました:
db.collection('collectionName').mapReduce(
function() {
for (var key in this) {
emit(key, null);
}
},
function(key, stuff) {
return null;
}, {
"out": "allFieldNames"
},
function(err, results) {
var fields = db.collection('allFieldNames').distinct('_id');
fields
.then(function(data) {
var finalData = {
"status": "success",
"fields": data
};
res.send(finalData);
delteCollection(db, 'allFieldNames');
})
.catch(function(err) {
res.send(err);
delteCollection(db, 'allFieldNames');
});
});
新しく作成したコレクション "allFieldNames"を読んだ後、それを削除します。
db.collection("allFieldNames").remove({}, function (err,result) {
db.close();
return;
});
これを実現するには、mongo jsファイルを使用します。下記のコードをgetCollectionName.jsファイルに追加し、次に示すようにLinuxのコンソールでjsファイルを実行します。
mongo --Host 192.168.1.135 getCollectionName.js
db_set = connect("192.168.1.135:27017/database_set_name"); // for Local testing
// db_set.auth("username_of_db", "password_of_db"); // if required
db_set.getMongo().setSlaveOk();
var collectionArray = db_set.getCollectionNames();
collectionArray.forEach(function(collectionName){
if ( collectionName == 'system.indexes' || collectionName == 'system.profile' || collectionName == 'system.users' ) {
return;
}
print("\nCollection Name = "+collectionName);
print("All Fields :\n");
var arrayOfFieldNames = [];
var items = db_set[collectionName].find();
// var items = db_set[collectionName].find().sort({'_id':-1}).limit(100); // if you want fast & scan only last 100 records of each collection
while(items.hasNext()) {
var item = items.next();
for(var index in item) {
arrayOfFieldNames[index] = index;
}
}
for (var index in arrayOfFieldNames) {
print(index);
}
});
quit();
ありがとう@ackuser
@James Cropchoの答えからのスレッドに続いて、私は非常に使いやすいことがわかった次のものに到達しました。これはバイナリツールであり、まさに私が探していたものです。 mongoeye 。
このツールを使用すると、コマンドラインからスキーマをエクスポートするのに約2分かかりました。