Ruft die Namen aller Schlüssel in der Sammlung ab

322

Ich möchte die Namen aller Schlüssel in einer MongoDB-Sammlung erhalten.

Zum Beispiel daraus:

db.things.insert( { type : ['dog', 'cat'] } );
db.things.insert( { egg : ['cat'] } );
db.things.insert( { type : [] } );
db.things.insert( { hello : []  } );

Ich möchte die eindeutigen Schlüssel erhalten:

type, egg, hello
Steve
quelle

Antworten:

346

Sie können dies mit MapReduce tun:

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"
})

Führen Sie dann die resultierende Sammlung eindeutig aus, um alle Schlüssel zu finden:

db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]
kristina
quelle
2
Hallo! Ich habe gerade ein Follow-up zu dieser Frage veröffentlicht, in dem gefragt wird, wie dieses Snippet auch mit Schlüsseln funktioniert, die sich auf tieferen Ebenen in der Datenstruktur befinden ( stackoverflow.com/questions/2997004/… ).
Andrea Fiore
1
@kristina: Wie ist es möglich, dass ich ganze bekommen Dinge mit den Tasten aufgeführt , wenn diese auf der Verwendung Dinge Sammlung. Es sieht im Zusammenhang mit dem Verlaufsmechanismus aus, weil ich Dinge bekomme, die ich in der Vergangenheit geändert habe.
Shawn
3
Ich weiß, dass dies ein alter Thread ist, aber ich habe anscheinend ein ähnliches Bedürfnis. Ich verwende den nativen mongodb-Treiber von nodejs. Die resultierende temporäre Sammlung scheint immer leer zu sein. Ich verwende dafür die mapreduce-Funktion in der Auflistungsklasse. Ist das nicht möglich
Deepak
6
Dies mag offensichtlich sein, aber wenn Sie eine Liste aller eindeutigen Schlüssel in einem Unterdokument erhalten möchten, ändern Sie einfach diese Zeile:for (var key in this.first_level.second_level.nth_level) { emit(key, null); }
dtbarne
3
Anstatt in einer Sammlung zu speichern, die dann eindeutig ausgeführt wird, verwende ich map ():db.runCommand({..., out: { "inline" : 1 }}).results.map(function(i) { return i._id; });
Ian Stanley
203

Mit Kristinas Antwort als Inspiration habe ich ein Open-Source-Tool namens Variety erstellt, das genau dies tut: https://github.com/variety/variety

James Cropcho
quelle
13
Dies ist ein fantastisches Tool, herzlichen Glückwunsch. Es macht genau das, was die Frage stellt, und kann mit Grenzen, Tiefe usw. konfiguriert werden. Empfohlen von jedem, der folgt.
Paul Biggar
74

Sie können die Aggregation mit neuer Verwendung $objectToArrrayin 3.4.4Version alle Top-Schlüssel und Wert - Paar in Dokument - Arrays von gefolgt zu konvertieren $unwindund $group mit $addToSetverschiedenen Schlüssel über die gesamte Sammlung zu erhalten.

$$ROOT zum Verweisen auf das Dokument der obersten Ebene.

db.things.aggregate([
  {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
  {"$unwind":"$arrayofkeyvalue"},
  {"$group":{"_id":null,"allkeys":{"$addToSet":"$arrayofkeyvalue.k"}}}
])

Sie können die folgende Abfrage verwenden, um Schlüssel in einem einzelnen Dokument abzurufen.

db.things.aggregate([
  {"$match":{_id: "5e8f968639bb8c67726686bc"}}, /* Replace with the document's ID */
  {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
  {"$project":{"keys":"$arrayofkeyvalue.k"}}
])
Sagar Veeram
quelle
20
Das ist wirklich die beste Antwort. Löst das Problem ohne eine andere Programmiersprache oder ein anderes Paket und funktioniert mit allen Treibern, die das aggregierte Framework unterstützen (sogar Meteor!)
Micah Henning
2
Wenn Sie ein Array anstelle eines Cursors zurückgeben möchten, der einen einzelnen Karteneintrag mit einem Schlüssel "allkeys" enthält, können Sie .next()["allkeys"]an den Befehl anhängen (vorausgesetzt, die Sammlung enthält mindestens ein Element).
M. Justin
19

Versuche dies:

doc=db.thinks.findOne();
for (key in doc) print(key);
Carlos LM
quelle
49
falsche Antwort, da hier nur Felder für ein einzelnes Dokument in einer Sammlung ausgegeben werden - die anderen haben möglicherweise alle völlig unterschiedliche Schlüssel.
Asya Kamsky
15
Es ist immer noch die nützlichste Antwort für mich, ein einfaches vernünftiges Minimum zu sein.
Boris Burkov
11
Es ist nicht nützlich? Wie ist es nützlich, wenn es Ihnen die falsche Antwort gibt?
Zlatko
4
Der Kontext zeigt, was nützlich ist: Wenn Daten normalisiert sind (z. B. aus der CSV-Datei), ist dies hilfreich ... Für aus SQL importierte Daten ist dies hilfreich.
Peter Krauss
5
Es ist keine gute Antwort. Es ist eine Antwort darauf, wie man Schlüssel eines Elements in der Sammlung erhält, nicht alle Schlüssel in der Sammlung!
Yonatan
16

Wenn Ihre Zielsammlung nicht zu groß ist, können Sie dies unter Mongo Shell Client versuchen:

var allKeys = {};

db.YOURCOLLECTION.find().forEach(function(doc){Object.keys(doc).forEach(function(key){allKeys[key]=1})});

allKeys;
Li Chunlin
quelle
hier, wie kann ich regExp für bestimmte Schlüssel geben, wenn ich sehen möchte?
TB.M
@ TB.M können Sie dies versuchen: db.configs.find (). ForEach (Funktion (doc) {Object.keys (doc) .forEach (Funktion (Schlüssel) {if (/YOURREGEXP/.test(key)) { allKeys [key] = 1}})});
Li Chunlin
Was bedeutet Test hier? Kannst du das bitte erklären?
TB.M
14

Eine gereinigte und wiederverwendbare Lösung mit 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')

Verwendungszweck:

get_keys('dbname', 'collection')
>> ['key1', 'key2', ... ]
Ingo Fischer
quelle
1
Funktioniert super. Endlich habe ich mein Problem gelöst ... dies ist die einfachste Lösung, die ich im Stapelüberlauf gesehen habe.
Smack Alpha
Und um nach Typ zu filtern, fügen Sie einfach zB if (typeof(this[key]) == 'number')vorher hinzu emit(key, null).
Skippy le Grand Gourou
10

Python verwenden. Gibt den Satz aller Schlüssel der obersten Ebene in der Sammlung zurück:

#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()
)
Laizer
quelle
1
Ich habe festgestellt, dass dies funktioniert, aber wie effizient ist es im Vergleich zu einer rohen Mongod-Abfrage?
Jesus Gomez
1
Ich bin mir ziemlich sicher, dass dies äußerst ineffizient ist, verglichen mit der direkten Ausführung in Mongodb
Ingo Fischer am
9

Hier ist das in Python bearbeitete Beispiel: Dieses Beispiel gibt die Ergebnisse inline zurück.

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']
BobHy
quelle
9

Wenn Sie Mongodb 3.4.4 und höher verwenden, können Sie die folgende Aggregation mit $objectToArrayund $groupAggregation verwenden

db.collection.aggregate([
  { "$project": {
    "data": { "$objectToArray": "$$ROOT" }
  }},
  { "$project": { "data": "$data.k" }},
  { "$unwind": "$data" },
  { "$group": {
    "_id": null,
    "keys": { "$addToSet": "$data" }
  }}
])

Hier ist das Arbeitsbeispiel

Ashh
quelle
Dies ist die beste Antwort. Sie können auch $matcham Anfang der Aggregationspipeline verwenden, um nur die Schlüssel von Dokumenten abzurufen, die einer Bedingung entsprechen.
RonquilloAeon
5

Ich bin überrascht, dass hier niemand eine Antwort hat, indem er einfach javascriptund Setlogisch die doppelten Werte automatisch filtert. Ein einfaches Beispiel für die Mongo-Shell ist 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)

Dadurch werden alle möglichen eindeutigen Schlüssel im Sammlungsnamen gedruckt : collectionName .

krishna Prasad
quelle
3

Das 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);
}
ackuser
quelle
3

Ich denke, der beste Weg, dies wie hier erwähnt zu tun , ist in Mongod 3.4.4+, aber ohne den $unwindOperator zu verwenden und nur zwei Stufen in der Pipeline zu verwenden. Stattdessen können wir die Operatoren $mergeObjectsund verwenden $objectToArray.

In der $groupPhase verwenden wir den $mergeObjectsOperator, um ein einzelnes Dokument zurückzugeben, bei dem Schlüssel / Wert aus allen Dokumenten in der Sammlung stammen.

Dann kommt der Ort, an $projectdem wir die Schlüssel verwenden $mapund $objectToArrayzurückgeben.

let allTopLevelKeys =  [
    {
        "$group": {
            "_id": null,
            "array": {
                "$mergeObjects": "$$ROOT"
            }
        }
    },
    {
        "$project": {
            "keys": {
                "$map": {
                    "input": { "$objectToArray": "$array" },
                    "in": "$$this.k"
                }
            }
        }
    }
];

Wenn wir nun verschachtelte Dokumente haben und auch die Schlüssel erhalten möchten, ist dies machbar. Betrachten Sie der Einfachheit halber ein Dokument mit einem einfachen eingebetteten Dokument, das folgendermaßen aussieht:

{field1: {field2: "abc"}, field3: "def"}
{field1: {field3: "abc"}, field4: "def"}

Die folgende Pipeline liefert alle Schlüssel (Feld1, Feld2, Feld3, Feld4).

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 ein wenig Aufwand können wir den Schlüssel für alle Unterdokumente in einem Array-Feld abrufen, in dem die Elemente ebenfalls Objekte sind.

Styvane
quelle
Ja, $unwindwird die Sammlung explodieren lassen (Anzahl der Felder * Anzahl der Dokumente). Wir können dies vermeiden, indem wir sie $mergeObjectsfür alle Versionen verwenden 3.6. -_-)
whoami
3

Möglicherweise etwas außerhalb des Themas, aber Sie können alle Schlüssel / Felder eines Objekts rekursiv hübsch drucken:

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 dieselbe Struktur haben.

qed
quelle
1

Um eine Liste aller Schlüssel minus zu erhalten _id, sollten Sie die folgende aggregierte Pipeline ausführen:

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"];
chridam
quelle
0

Ich habe versucht, in nodejs zu schreiben und habe mir schließlich Folgendes ausgedacht:

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');
        });
 });

Löschen Sie die neu erstellte Sammlung "allFieldNames", nachdem Sie sie gelesen haben.

db.collection("allFieldNames").remove({}, function (err,result) {
     db.close();
     return; 
});
Gautam
quelle
0

Gemäß der Mongoldb- Dokumentation ist eine Kombination ausdistinct

Findet die unterschiedlichen Werte für ein angegebenes Feld in einer einzelnen Sammlung oder Ansicht und gibt die Ergebnisse in einem Array zurück.

und Indexerfassungsoperationen geben alle möglichen Werte für einen bestimmten Schlüssel oder Index zurück:

Gibt ein Array zurück, das eine Liste von Dokumenten enthält, die die vorhandenen Indizes für die Sammlung identifizieren und beschreiben

In einer bestimmten Methode könnte man also eine Methode wie die folgende verwenden, um eine Sammlung nach allen registrierten Indizes abzufragen und beispielsweise ein Objekt mit den Indizes für Schlüssel zurückzugeben (in diesem Beispiel wird async / await für NodeJS verwendet, aber Natürlich können Sie auch einen 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;
        }
    }
}

Wenn Sie also eine Sammlung mit dem Basisindex abfragen _id, wird Folgendes zurückgegeben (die Testsammlung enthält zum Zeitpunkt des Tests nur ein Dokument):

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..
        })
});

Allerdings werden hierfür Methoden verwendet, die dem NodeJS-Treiber eigen sind. Wie einige andere Antworten vorgeschlagen haben, gibt es andere Ansätze, wie beispielsweise das aggregierte Framework. Ich persönlich finde diesen Ansatz flexibler, da Sie auf einfache Weise erstellen und optimieren können, wie die Ergebnisse zurückgegeben werden sollen. Dies betrifft natürlich nur Attribute der obersten Ebene, keine verschachtelten. Um sicherzustellen, dass alle Dokumente dargestellt werden, falls es Sekundärindizes gibt (außer dem Haupt-ID), sollten diese Indizes wie folgt festgelegt werden required.

jlmurph
quelle
0

Wir können dies erreichen, indem wir die mongo js-Datei verwenden. Fügen Sie den folgenden Code in Ihre getCollectionName.js- Datei ein und führen Sie die js-Datei in der Linux-Konsole wie folgt aus:

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();

Danke @ackuser

Irshad Khan
quelle
0

Nach dem Thread aus der Antwort von @James Cropcho bin ich auf Folgendes gestoßen, das ich als super einfach zu bedienen empfand. Es ist ein binäres Werkzeug, genau das, wonach ich gesucht habe: Mongoeye .

Mit diesem Tool dauerte es ungefähr 2 Minuten, bis mein Schema von der Befehlszeile exportiert wurde.

Paneer Tikka
quelle
0

Ich weiß, dass diese Frage 10 Jahre alt ist, aber es gibt keine C # -Lösung, und ich habe Stunden gebraucht, um das herauszufinden. Ich verwende den .NET-Treiber und System.Linqgebe eine Liste der Schlüssel zurück.

var map = new BsonJavaScript("function() { for (var key in this) { emit(key, null); } }");
var reduce = new BsonJavaScript("function(key, stuff) { return null; }");
var options = new MapReduceOptions<BsonDocument, BsonDocument>();
var result = await collection.MapReduceAsync(map, reduce, options);
var list = result.ToEnumerable().Select(item => item["_id"].ToString());
Andrew Samole
quelle
-1

Ich habe die Lösung von Carlos LM etwas erweitert, damit sie detaillierter ist.

Beispiel eines Schemas:

var schema = {
    _id: 123,
    id: 12,
    t: 'title',
    p: 4.5,
    ls: [{
            l: 'lemma',
            p: {
                pp: 8.9
            }
        },
         {
            l: 'lemma2',
            p: {
               pp: 8.3
           }
        }
    ]
};

Geben Sie in die Konsole ein:

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;
        }
    }
}

Lauf:

schemafy(db.collection.findOne());

Ausgabe

_id <number>
id <number>
t <string>
p <number>
ls <object>:
    0 <object>:
    l <string>
    p <object>:
        pp <number> 
va5ja
quelle
3
Seine Antwort ist falsch und Sie haben darauf aufgebaut. Der springende Punkt ist, alle Felder aller Dokumente auszugeben , nicht das erste Dokument, das andere Felder als jedes nächste haben kann.
Asya Kamsky
-3

Ich habe 1 einfachere Arbeit zu umgehen ...

Was Sie tun können, ist, während Sie Daten / Dokumente in Ihre Hauptsammlung "Dinge" einfügen. Sie müssen die Attribute in eine separate Sammlung einfügen, sagen wir "Dinge_Attribute".

Jedes Mal, wenn Sie in "things" einfügen, erhalten Sie von "things_attributes" einen Vergleich der Werte dieses Dokuments mit Ihren neuen Dokumentschlüsseln, wenn ein neuer vorhandener Schlüssel ihn in dieses Dokument anfügt und erneut einfügt.

Things_attributes verfügt also nur über 1 Dokument mit eindeutigen Schlüsseln, die Sie mithilfe von findOne () jederzeit problemlos abrufen können.

Paresh Behede
quelle
Bei Datenbanken mit vielen Einträgen, bei denen Abfragen für alle Schlüssel häufig und Einfügungen selten sind, ist es sinnvoll, das Ergebnis der Abfrage "Alle Schlüssel abrufen" zwischenzuspeichern. Dies ist eine Möglichkeit, dies zu tun.
Scott