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
War es hilfreich?

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top