Erhalten Namen aller Schlüssel in der Sammlung
-
21-09-2019 - |
Frage
Ich mag die Namen aller Schlüssel in einer MongoDB Sammlung erhalten.
Zum Beispiel, von hier:
db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { egg : ['cat'] } );
db.things.insert( { type : [] } );
db.things.insert( { hello : [] } );
Ich mag die eindeutigen Schlüssel erhalten:
type, egg, hello
Lösung
Sie können dies tun, mit 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"
})
Dann führte verschieden auf der resultierende Sammlung, um alle Schlüssel zu finden:
db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]
Andere Tipps
Mit Kristina Antwort als Inspiration, habe ich ein Open-Source-Tool namens Variety, die genau dies tut: https://github.com/variety/variety
Sie können mit Aggregation mit neuen $objectToArrray
in 3.4.4
Version all Top-Taste & Wert-Paar in Dokumenten-Arrays konvertieren gefolgt von $unwind
& $group
mit $addToSet
verschiedene Schlüssel über die gesamte Sammlung zu erhalten.
$$ROOT
für das Top-Level-Dokument verwiesen wird.
db.things.aggregate([
{"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
{"$unwind":"$arrayofkeyvalue"},
{"$group":{"_id":null,"allkeys":{"$addToSet":"$arrayofkeyvalue.k"}}}
])
Sie können unter Abfrage für Schlüssel in einem einzigen Dokument zu bekommen.
db.things.aggregate([
{"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
{"$project":{"keys":"$arrayofkeyvalue.k"}}
])
Versuchen Sie diese:
doc=db.thinks.findOne();
for (key in doc) print(key);
Wenn Sie Ihre Zielsammlung nicht zu groß ist, können Sie versuchen, diese unter Mongo Shell-Client:
var allKeys = {};
db.YOURCOLLECTION.find().forEach(function(doc){Object.keys(doc).forEach(function(key){allKeys[key]=1})});
allKeys;
Mit Python. Gibt die Menge aller Top-Level-Schlüssel in der Sammlung:
#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()
)
Hier ist die Probe in Python gearbeitet: Dieses Beispiel gibt die Ergebnisse inline.
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']
A gereinigt und wiederverwendbare Lösung pymongo mit:
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')
Verbrauch:
get_keys('dbname', 'collection')
>> ['key1', 'key2', ... ]
Wenn Sie mit mongodb 3.4.4 und höher, dann können Sie unter Aggregation verwenden mit $objectToArray
und < strong> $group
Aggregation
db.collection.aggregate([
{ "$project": {
"data": { "$objectToArray": "$$ROOT" }
}},
{ "$project": { "data": "$data.k" }},
{ "$unwind": "$data" },
{ "$group": {
"_id": null,
"keys": { "$addToSet": "$data" }
}}
])
Hier ist die Arbeits Beispiel:
Dies funktioniert gut für mich:
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);
}
Ich bin überrascht, niemand hat hier am mit einfachen javascript
und Set
Logik, um automatisch die Duplikate Werte filtern, einfaches Beispiel auf Mongo Shell wie folgt:
var allKeys = new Set()
db.collectionName.find().forEach( function (o) {for (key in o ) allKeys.add(key)})
for(let key of allKeys) print(key)
Damit werden alle möglichen eindeutigen Tasten drucken in der Sammlung Name:. collection
Um eine Liste aller Schlüssel minus _id
zu erhalten, sollten Sie die folgende Aggregat Pipeline:
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"];
Ich denke, der beste Weg, dies zu tun, wie hier erwähnt ist in mongod 3.4.4+ aber ohne den $unwind
Operator und unter Verwendung von nur zwei Stufen in der Pipeline. Stattdessen können wir verwenden die $mergeObjects
und $objectToArray
Operatoren.
In der $group
Stufe verwenden wir die $mergeObjects
Bediener ein einzelnes Dokument zurückzukehren, in den Schlüssel / Wert aus allen Dokumenten in der Sammlung ist.
Dann kommt die $project
wo wir $map
und $objectToArray
mit den Tasten zurückzukehren.
let allTopLevelKeys = [
{
"$group": {
"_id": null,
"array": {
"$mergeObjects": "$$ROOT"
}
}
},
{
"$project": {
"keys": {
"$map": {
"input": { "$objectToArray": "$array" },
"in": "$$this.k"
}
}
}
}
];
Nun, wenn wir ein verschachtelten Dokumente haben und wollen auch die Schlüssel zu bekommen, ist dies machbar. Der Einfachheit halber lassen Sie ein Dokument mit einfachen eingebettete Dokument der Ansicht, dass wie folgt aussehen:
{field1: {field2: "abc"}, field3: "def"}
{field1: {field3: "abc"}, field4: "def"}
Die folgende Pipeline Ausbeute alle Tasten (feld1, 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"
}
}
]
}
}
}
]
Mit einem wenig Aufwand können wir den Schlüssel für alle Subdokument in einem Array Feld, wo die Elemente als auch Objekt sind.
Ich habe versucht, in NodeJS zu schreiben und kam schließlich mit diesem nach oben:
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');
});
});
Nachdem die neu erstellten Sammlung "allFieldNames" zu lesen, löschen Sie es.
db.collection("allFieldNames").remove({}, function (err,result) {
db.close();
return;
});
Gemäß der mongoldb Dokumentation , eine Kombination aus distinct
Findet die unterschiedlichen Werte für einen bestimmten Bereich in einer einzigen Sammlung oder Anzeigen und gibt die Ergebnisse in einem Array.
und Indizes Sammlung Operationen sind, was würden zurückkehren alle möglichen Werte für einen bestimmten Schlüssel oder Index:
Gibt einen Array, das eine Liste von Dokumenten enthält, die die vorhandenen Indizes für die Sammlung identifizieren und beschreiben
So in einem bestimmten Verfahren könnte man ein Verfahren wie die folgenden tun verwenden, um eine Sammlung für alle abfragen es Indizes registriert, und zurück, sagt ein Objekt mit dem Indizes für Schlüssel (in diesem Beispiel verwendet async / await für NodeJS, aber natürlich könnte man anderen asynchronen Ansatz verwenden):
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;
}
}
}
So eine Sammlung mit dem Grunde _id
Index Abfrage zurückkehren würde die folgende (Testsammlung gibt es nur ein Dokument zum Zeitpunkt des Tests):
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..
})
});
Wohlgemerkt, diese Anwendungen Methoden stammen aus dem NodeJS Treiber. Wie einige andere Antworten vorgeschlagen haben, gibt es andere Ansätze, wie das Aggregat Rahmen. Ich persönlich finde diesen Ansatz flexibler zu gestalten, wie Sie ganz einfach erstellen und optimieren, wie die Ergebnisse zurückzukehren. Offensichtlich ist dies nur Adressen Top-Level-Attribute, keine verschachtelt diejenigen.
Auch zu garantieren, dass alle Dokumente dargestellt werden sollten Sekundärindizes (außer dem Haupt _id eins) da sein, sollten diese Indizes als required
eingestellt werden.
Vielleicht etwas off-topic, aber Sie können alle Tasten / Felder eines Objekts rekursiv Pretty-Print:
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)
}
Nützlich, wenn alle Objekte in einer Sammlung die gleiche Struktur haben.
Wir können dies erreichen, indem Sie Mongo js-Datei. Fügen Sie unter Code in den getCollectionName.js Datei und starten Sie js Datei in der Konsole von Linux wie unten angegeben:
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();
Dank @ackuser
Sie den Faden von @James Cropcho Antwort Folgen, landete ich auf dem nächsten, die ich super einfach zu bedienen erwiesen. Es ist ein binäres Werkzeug, das ist genau das, was ich gesucht habe: mongoeye .
Mit diesem Tool es etwa 2 Minuten in Anspruch nahm mein Schema von der Kommandozeile ausgeführt erhalten.
erweiterte I Carlos LM-Lösung ein wenig, so dass es mehr detailliert beschrieben wird.
Beispiel für ein Schema:
var schema = {
_id: 123,
id: 12,
t: 'title',
p: 4.5,
ls: [{
l: 'lemma',
p: {
pp: 8.9
}
},
{
l: 'lemma2',
p: {
pp: 8.3
}
}
]
};
Typ in die Konsole:
var schemafy = function(schema, i, limit) {
var i = (typeof i !== 'undefined') ? i : 1;
var limit = (typeof limit !== 'undefined') ? limit : false;
var type = '';
var array = false;
for (key in schema) {
type = typeof schema[key];
array = (schema[key] instanceof Array) ? true : false;
if (type === 'object') {
print(Array(i).join(' ') + key+' <'+((array) ? 'array' : type)+'>:');
schemafy(schema[key], i+1, array);
} else {
print(Array(i).join(' ') + key+' <'+type+'>');
}
if (limit) {
break;
}
}
}
Ausführen:
schemafy(db.collection.findOne());
Output
_id <number>
id <number>
t <string>
p <number>
ls <object>:
0 <object>:
l <string>
p <object>:
pp <number>
Ich habe eine einfache Arbeit um ...
Was Sie tun können, ist, während die Daten / das Dokument in Ihre Hauptsammlung „Dinge“ Einfügen Sie die Attribute in 1 separate Sammlung einfügen müssen können „things_attributes“ sagen.
, so dass Sie jedes Mal in „Dinge“ einzufügen, die Sie von „things_attributes“ erhalten Sie vergleichen Werte dieses Dokument mit Ihrem neuen Dokument Schlüssel, wenn eine neue Schlüssel vorhanden es in diesem Dokument anhängen und wieder wieder einsetzen.
So things_attributes nur 1 Dokument des eindeutigen Schlüssels, die Sie leicht bekommen können, wann immer erforderlich, dass Sie durch die Verwendung FindOne ()