wake-up-neo.com

Ruft die Namen aller Schlüssel in der Sammlung ab

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

Zum Beispiel davon:

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
278
Steve

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", ...]
304
kristina

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

187
James Cropcho

Sie können die Aggregation mit der neuen $objectToArrray in 3.4.4 version verwenden, um alle Paare aus Schlüssel und Wert in Dokumentarrays zu konvertieren, gefolgt von $unwind & $group mit $addToSet , um zu erhalten verschiedene Schlüssel über die gesamte Sammlung.

$$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 Abfrage unten verwenden, um Schlüssel in einem einzelnen Dokument abzurufen.

db.things.aggregate([
  {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
  {"$project":{"keys":"$arrayofkeyvalue.k"}}
])
38
Veeram

Versuche dies:

doc=db.thinks.findOne();
for (key in doc) print(key);
22
Carlos LM

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

var allKeys = {};

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

allKeys;
13
Li Chunlin

Python verwenden. Gibt die Menge aller Schlüssel der obersten Ebene in der Auflistung 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()
)
10
Laizer

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

Wenn Sie mongodb 3.4.4 oder höher verwenden, können Sie die folgende Aggregation mit $objectToArray und $group aggregation verwenden

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

Hier ist das funktionierende Beispiel

2
Anthony Winzlet

Eine aufgeräumte 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', ... ]
2
ifischer

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);
}
2
ackuser

Ich denke, der beste Weg, wie gesagt hier ist in mongod 3.4.4+, aber ohne den $unwind-Operator und nur zwei Stufen in der Pipeline. Stattdessen können wir die Operatoren $mergeObjects und $objectToArray verwenden.

In der Phase $group verwenden wir den Operator $mergeObjects, um ein einzelnes Dokument zurückzugeben, dessen Schlüssel/Wert aus allen Dokumenten in der Sammlung stammt.

Dann kommt der $project, wo wir $map und $objectToArray verwenden, um die Schlüssel zurückzugeben.

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. Zur Vereinfachung betrachten wir ein Dokument mit einem einfachen eingebetteten Dokument, das folgendermaßen aussieht:

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

Die folgende Pipeline enthält 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 etwas Aufwand können wir den Schlüssel für alle Filialdokumente in einem Array-Feld erhalten, in dem die Elemente ebenfalls Objekte sind. 

1
styvane

Ich bin überrascht, dass hier niemand eine einfache javascript- und Set-Logik verwendet, um die doppelten Werte automatisch zu filtern. Ein einfaches Beispiel für 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)

Dadurch werden alle möglichen eindeutigen Schlüssel im Sammlungsnamen ausgegeben: Sammlungsname .

1
krishna Prasad

Um eine Liste aller Schlüssel minus _id zu erhalten, sollten Sie die folgende Aggregatpipeline 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"];
1
chridam

Vielleicht etwas außerhalb des Themas, aber Sie können alle Schlüssel/Felder eines Objekts rekursiv 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 die gleiche Struktur haben. 

0
qed

Laut mongoldb documentation eine Kombination aus distinct

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

und indexes collection-Operationen würden alle möglichen Werte für einen bestimmten Schlüssel oder Index zurückgeben:

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

In einer gegebenen Methode könnte man also eine Methode wie die folgende verwenden, um eine Collection nach allen registrierten Indizes abzufragen und 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 _id abfragen, wird Folgendes zurückgegeben (die Testsammlung hat 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 für den NodeJS-Treiber native Methoden verwendet. Wie einige andere Antworten vorgeschlagen haben, gibt es andere Ansätze wie das Aggregat-Framework. Ich persönlich finde diesen Ansatz flexibler, da Sie die Ergebnisse auf einfache Weise erstellen und verfeinern können. Offensichtlich werden hier nur Attribute der obersten Ebene behandelt, nicht geschachtelte Attribute. Um sicherzustellen, dass alle Dokumente dargestellt werden, falls sekundäre Indizes (außer der Haupt-_id-Nummer) vorhanden sind, sollten diese Indizes als required festgelegt werden.

0
jlmurph

Ich habe versucht, in nodejs zu schreiben, und kam schließlich dazu:

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 Sie die neu erstellte Sammlung "allFieldNames" gelesen haben, löschen Sie sie.

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

Wir können dies erreichen, indem Sie 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

0
Irshad Khan

Dem Thread aus der Antwort von @James Cropcho folgend, bin ich auf den folgenden gelandet, den ich als super einfach zu benutzen empfand. Es ist ein binäres Werkzeug, nach dem ich genau gesucht habe: mongoeye .

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

0
paneer_tikka