Lấy tên của tất cả các khóa trong bộ sưu tập


322

Tôi muốn lấy tên của tất cả các khóa trong bộ sưu tập MongoDB.

Ví dụ: từ đây:

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

Tôi muốn nhận các khóa duy nhất:

type, egg, hello

Câu trả lời:


346

Bạn có thể làm điều này với 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"
})

Sau đó chạy riêng biệt trên bộ sưu tập kết quả để tìm tất cả các khóa:

db[mr.result].distinct("_id")
["foo", "bar", "baz", "_id", ...]

2
Chào bạn Tôi vừa đăng một câu hỏi tiếp theo cho câu hỏi này làm thế nào để làm cho đoạn mã này hoạt động ngay cả với các khóa nằm ở cấp độ sâu hơn trong cấu trúc dữ liệu ( stackoverflow.com/questions/2997004/ Lỗi ).
Andrea Fiore

1
@kristina: Làm thế nào có thể tôi có được toàn bộ những thứ được liệt kê với các phím khi sử dụng cái này trên bộ sưu tập thứ . Nó có vẻ liên quan đến cơ chế lịch sử bởi vì tôi nhận được những thứ mà tôi đã sửa đổi trong quá khứ ..
Shawn

3
Tôi biết đây là một chủ đề cũ, nhưng tôi dường như có một nhu cầu tương tự. Tôi đang sử dụng trình điều khiển gốc mongjb của nodejs. Bộ sưu tập tạm thời kết quả dường như trống rỗng luôn. Tôi đang sử dụng hàm mapreduce trong lớp bộ sưu tập cho việc này. Điều đó là không thể?
Deepak

6
Điều này có thể rõ ràng, nhưng nếu bạn muốn có một danh sách tất cả các khóa duy nhất trong một văn bản con, chỉ cần sửa đổi dòng này:for (var key in this.first_level.second_level.nth_level) { emit(key, null); }
dtbarne

3
Thay vì lưu vào bộ sưu tập sau đó chạy khác biệt, tôi sử dụng map ():db.runCommand({..., out: { "inline" : 1 }}).results.map(function(i) { return i._id; });
Ian Stanley


74

Bạn có thể sử dụng kết hợp với mới $objectToArrraytrong 3.4.4phiên bản để chuyển đổi tất cả các chìa khóa top & cặp giá trị vào mảng tài liệu tiếp theo $unwind& $group $addToSetđể có được chìa khóa riêng biệt trên toàn bộ bộ sưu tập.

$$ROOT để tham khảo tài liệu cấp cao nhất.

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

Bạn có thể sử dụng truy vấn dưới đây để nhận khóa trong một tài liệu.

db.things.aggregate([
  {"$match":{_id: "5e8f968639bb8c67726686bc"}}, /* Replace with the document's ID */
  {"$project":{"arrayofkeyvalue":{"$objectToArray":"$$ROOT"}}},
  {"$project":{"keys":"$arrayofkeyvalue.k"}}
])

20
Đây thực sự là câu trả lời tốt nhất. Giải quyết vấn đề mà không liên quan đến một số ngôn ngữ lập trình hoặc gói khác và hoạt động với tất cả các trình điều khiển hỗ trợ khung tổng hợp (thậm chí cả sao băng!)
Micah Henning

2
Nếu bạn muốn trả về một mảng thay vì một con trỏ chứa một mục nhập bản đồ duy nhất bằng phím "allkeys", bạn có thể nối thêm .next()["allkeys"]lệnh (giả sử bộ sưu tập có ít nhất một phần tử).
M. Justin

19

Thử cái này:

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

49
câu trả lời không chính xác vì điều này chỉ xuất ra các trường cho một tài liệu trong một bộ sưu tập - những cái khác có thể có các khóa hoàn toàn khác nhau.
Asya Kamsky

15
Nó vẫn là câu trả lời hữu ích nhất đối với tôi, là một mức tối thiểu hợp lý đơn giản.
Boris Burkov

11
Nó không hữu ích? Làm thế nào là hữu ích nếu nó cung cấp cho bạn câu trả lời sai?
Zlatko

4
Ngữ cảnh hiển thị những gì hữu ích: nếu dữ liệu được chuẩn hóa (ví dụ: nguồn gốc từ tệp CSV), thì nó rất hữu ích ... Đối với dữ liệu được nhập từ SQL là hữu ích.
Peter Krauss

5
đó không phải là một câu trả lời hay, đó là một câu trả lời về cách lấy các khóa của một yếu tố trong bộ sưu tập chứ không phải tất cả các khóa trong bộ sưu tập!
yonatan

16

Nếu bộ sưu tập mục tiêu của bạn không quá lớn, bạn có thể thử điều này trong ứng dụng khách mongo shell:

var allKeys = {};

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

allKeys;

Ở đây làm thế nào tôi có thể cung cấp regExp cho các khóa cụ thể nếu tôi muốn xem?
TB.M

@ TB.M bạn có thể thử điều này: db.configs.find (). ForEach (function (doc) {Object.keys (doc) .forEach (function (key) {if (/YOURREGEXP/.test(key)) { allKeys [key] = 1}})});
Li Chunlin

kiểm tra có nghĩa là gì ở đây? bạn có thể giải thích không?
TB.M


14

Một giải pháp làm sạch và tái sử dụng bằng 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')

Sử dụng:

get_keys('dbname', 'collection')
>> ['key1', 'key2', ... ]

1
Công trình tuyệt vời. Cuối cùng cũng giải quyết được vấn đề của tôi .... đây là giải pháp đơn giản nhất tôi thấy trong stack stack ..
Smack Alpha

Và để lọc theo loại, chỉ cần thêm ví dụ if (typeof(this[key]) == 'number')trước emit(key, null).
Skippy le Grand Gourou

10

Sử dụng trăn. Trả về tập hợp tất cả các khóa cấp cao nhất trong bộ sưu tập:

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

1
Tôi đã tìm thấy điều này để làm việc nhưng nó hiệu quả như thế nào so với một truy vấn mongod thô?
Jesus Gomez

1
Tôi khá chắc chắn rằng điều này cực kỳ kém hiệu quả so với việc thực hiện trực tiếp trong Mongodb
Ingo Fischer

9

Đây là mẫu làm việc trong Python: Mẫu này trả về kết quả nội tuyến.

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

9

Nếu bạn đang sử dụng mongodb 3.4.4 trở lên thì bạn có thể sử dụng tổng hợp bên dưới bằng cách sử dụng $objectToArray$grouptổng hợp

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

Dưới đây là ví dụ làm việc


Đây là câu trả lời tốt nhất. Bạn cũng có thể sử dụng $matchở đầu đường ống tổng hợp để chỉ lấy các khóa của tài liệu khớp với (các) điều kiện.
RonquilloAeon

5

Tôi ngạc nhiên, không ai ở đây có ans bằng cách sử dụng đơn giản javascriptSetlogic để tự động lọc các giá trị trùng lặp, ví dụ đơn giản trên vỏ mongo như dưới đây:

var allKeys = new Set()
db.collectionName.find().forEach( function (o) {for (key in o ) allKeys.add(key)})
for(let key of allKeys) print(key)

Điều này sẽ in tất cả các khóa duy nhất có thể có trong tên bộ sưu tập : CollectionName .


3

Cái này làm việc tốt cho tôi:

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

3

Tôi nghĩ rằng cách tốt nhất để làm điều này như được đề cập ở đây là trong mongod 3.4.4+ nhưng không sử dụng $unwindtoán tử và chỉ sử dụng hai giai đoạn trong đường ống. Thay vào đó chúng ta có thể sử dụng $mergeObjectsvà các $objectToArraytoán tử.

Trong $groupgiai đoạn, chúng tôi sử dụng $mergeObjectstoán tử để trả về một tài liệu duy nhất trong đó khóa / giá trị là từ tất cả các tài liệu trong bộ sưu tập.

Sau đó đến $projectnơi chúng tôi sử dụng $map$objectToArraytrả lại các phím.

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

Bây giờ nếu chúng ta có một tài liệu lồng nhau và muốn lấy các khóa là tốt, điều này là có thể làm được. Để đơn giản, hãy xem xét một tài liệu với tài liệu nhúng đơn giản trông như thế này:

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

Các đường ống sau đây mang lại tất cả các khóa (trường1, trường2, trường3, trường4).

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

Với một chút nỗ lực, chúng ta có thể nhận được khóa cho tất cả các văn bản con trong trường mảng trong đó các phần tử cũng là đối tượng.


$unwindsẽ làm nổ bộ sưu tập (các trường no.of * no.of doc), chúng ta có thể tránh điều đó bằng cách sử dụng $mergeObjectstrên tất cả các phiên bản> 3.6.. Đã làm như vậy, nên đã thấy câu trả lời này trước đây, cuộc sống của tôi sẽ dễ dàng hơn theo cách đó ( -_-)
whoami

3

Có thể hơi lạc đề, nhưng bạn có thể in đệ quy tất cả các khóa / trường của một đối tượng:

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

Hữu ích khi tất cả các đối tượng trong một bộ sưu tập có cùng cấu trúc.


1

Để có được danh sách tất cả các phím trừ _id, hãy xem xét chạy đường ống tổng hợp sau:

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

Tôi đã cố gắng viết trong nodejs và cuối cùng đã nghĩ ra điều này:

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

Sau khi đọc bộ sưu tập mới được tạo "allFieldNames", hãy xóa nó.

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

0

Theo tài liệu mongoldb , một sự kết hợp củadistinct

Tìm các giá trị riêng biệt cho một trường được chỉ định trong một bộ sưu tập hoặc chế độ xem và trả về kết quả trong một mảng.

các hoạt động thu thập chỉ mục là những gì sẽ trả về tất cả các giá trị có thể cho một khóa hoặc chỉ mục đã cho:

Trả về một mảng chứa danh sách các tài liệu xác định và mô tả các chỉ mục hiện có trên bộ sưu tập

Vì vậy, trong một phương thức đã cho, người ta có thể sử dụng một phương thức như phương pháp sau, để truy vấn một bộ sưu tập cho tất cả các chỉ mục đã đăng ký và trả về, nói một đối tượng có các chỉ mục cho các khóa (ví dụ này sử dụng async / await cho NodeJS, nhưng rõ ràng bạn có thể sử dụng bất kỳ phương pháp không đồng bộ nào khác):

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

Vì vậy, truy vấn một bộ sưu tập với _idchỉ mục cơ bản , sẽ trả về các mục sau (bộ sưu tập kiểm tra chỉ có một tài liệu tại thời điểm kiểm tra):

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

Xin lưu ý bạn, điều này sử dụng các phương thức có nguồn gốc từ Trình điều khiển NodeJS. Như một số câu trả lời khác đã đề xuất, có những cách tiếp cận khác, chẳng hạn như khung tổng hợp. Cá nhân tôi thấy cách tiếp cận này linh hoạt hơn, vì bạn có thể dễ dàng tạo và tinh chỉnh cách trả về kết quả. Rõ ràng, điều này chỉ giải quyết các thuộc tính cấp cao nhất, không phải các thuộc tính lồng nhau. Ngoài ra, để đảm bảo rằng tất cả các tài liệu được trình bày nên có các chỉ mục phụ (không phải là chính _id), các chỉ mục đó phải được đặt thành required.


0

Chúng ta có thể đạt được điều này bằng cách sử dụng tập tin mongo js. Thêm mã dưới đây vào tệp getCollectionName.js của bạn và chạy tệp js trong bảng điều khiển của Linux như được đưa ra dưới đây:

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

Cảm ơn @ackuser


0

Theo chủ đề từ câu trả lời của @James Cropcho, tôi đã tìm ra những điều sau đây mà tôi thấy là siêu dễ sử dụng. Nó là một công cụ nhị phân, chính xác là thứ tôi đang tìm kiếm: mongoeye .

Sử dụng công cụ này mất khoảng 2 phút để lược đồ của tôi được xuất từ ​​dòng lệnh.


0

Tôi biết câu hỏi này đã 10 tuổi nhưng không có giải pháp C # và điều này khiến tôi mất hàng giờ để tìm ra. Tôi đang sử dụng trình điều khiển .NET và System.Linqđể trả về danh sách các khóa.

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

-1

Tôi đã mở rộng giải pháp của Carlos LM một chút để nó chi tiết hơn.

Ví dụ về lược đồ:

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

Nhập vào bảng điều khiển:

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

Chạy:

schemafy(db.collection.findOne());

Đầu ra

_id <number>
id <number>
t <string>
p <number>
ls <object>:
    0 <object>:
    l <string>
    p <object>:
        pp <number> 

3
Câu trả lời của anh ấy là sai và bạn xây dựng trên nó. toàn bộ vấn đề là xuất ra tất cả các trường của tất cả các tài liệu, không phải là tài liệu đầu tiên có thể có các trường khác nhau so với mỗi trường tiếp theo.
Asya Kamsky

-3

Tôi có 1 công việc đơn giản hơn ...

Những gì bạn có thể làm là trong khi chèn dữ liệu / tài liệu vào bộ sưu tập chính của mình, "những thứ" bạn phải chèn các thuộc tính trong 1 bộ sưu tập riêng biệt, hãy nói "Things_attribut".

do đó, mỗi khi bạn chèn vào "thứ", bạn sẽ nhận được từ "Things_attribut" so sánh các giá trị của tài liệu đó với các khóa tài liệu mới của bạn nếu có bất kỳ khóa mới nào xuất hiện trong tài liệu đó và chèn lại nó.

Vì vậy, Things_attribut sẽ chỉ có 1 tài liệu về các khóa duy nhất mà bạn có thể dễ dàng nhận được khi bạn yêu cầu bằng cách sử dụng findOne ()


Đối với cơ sở dữ liệu có nhiều mục nhập trong đó các truy vấn cho tất cả các khóa là thường xuyên và việc chèn không thường xuyên, việc lưu trữ kết quả của truy vấn "lấy tất cả các khóa" sẽ có ý nghĩa. Đây là một cách để làm điều đó.
Scott
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.