Получить имена всех ключей в коллекции - PullRequest
294 голосов
/ 19 февраля 2010

Я хотел бы получить имена всех ключей в коллекции MongoDB.

Например, из этого:

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

Я бы хотел получить уникальные ключи:

type, egg, hello

Ответы [ 19 ]

2 голосов
/ 04 мая 2016

Это прекрасно работает для меня:

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);
}
1 голос
/ 11 апреля 2018

Я думаю, что лучший способ сделать это, как упоминалось, здесь в mongod 3.4.4+, но без использования оператора $unwind и использования только двух стадий в конвейере. Вместо этого мы можем использовать операторы $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"
                        }
                    }
                ]
            }
        }
    }
]

Приложив немного усилий, мы можем получить ключ для всего поддокумента в поле массива, где элементы также являются объектами.

1 голос
/ 26 февраля 2018

Чтобы получить список всех ключей минус _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"];
0 голосов
/ 25 декабря 2018

Мы можем добиться этого, используя mongo js file.Добавьте следующий код в файл getCollectionName.js и запустите файл js в консоли Linux, как указано ниже:

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

0 голосов
/ 12 октября 2018

Может быть, немного не по теме, но вы можете рекурсивно красиво распечатать все ключи / поля объекта:

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

Полезно, когда все объекты в коллекции имеют одинаковую структуру.

0 голосов
/ 15 мая 2018

Согласно документации mongoldb , комбинация distinct

Находит отличительные значения для указанного поля в одной коллекции или представлении и возвращает результаты в видемассив.

и индексы операции сбора - это то, что возвращает все возможные значения для данного ключа или индекса:

Возвращает массив, который содержитсписок документов, которые идентифицируют и описывают существующие индексы в коллекции

Таким образом, в данном методе можно использовать метод, подобный следующему, для запроса коллекции для всех зарегистрированных индексови возвращаем, скажем, объект с индексами для ключей (в этом примере для NodeJS используется async / await, но, очевидно, вы можете использовать любой другой асинхронный подход):

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

Итак, запрос коллекции с базовым1021 * index, будет возвращать следующее (тестовая коллекция имеет только один документ на момент теста):

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.

0 голосов
/ 10 октября 2017

Я пытался написать в 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; 
});
0 голосов
/ 28 марта 2014

Я немного расширил решение Carlos LM, чтобы оно было более подробным.

Пример схемы:

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

Введите в консоли:

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

Выполнить:

schemafy(db.collection.findOne());

Выход

_id <number>
id <number>
t <string>
p <number>
ls <object>:
    0 <object>:
    l <string>
    p <object>:
        pp <number> 
0 голосов
/ 21 марта 2014

У меня есть 1 более простая работа ...

Что вы можете сделать, вставляя данные / документ в основную коллекцию «вещей», вы должны вставить атрибуты в 1 отдельную коллекцию, скажем, «things_attributes».

так что каждый раз, когда вы вставляете в "things", вы получаете из "things_attributes" сравнение значений этого документа с вашими новыми ключами документа, если какой-либо новый присутствующий ключ добавляет его в этот документ и снова вставляет его снова.

Таким образом, в things_attributes будет только 1 документ с уникальными ключами, который вы можете легко получить в любое время с помощью findOne ()

...