MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Consultar e filtrar nomes de chaves em vez de valores no MongoDB


Você pode fazer isso usando mapReduce :

Para obter apenas os nomes dos campos no nível raiz:
db.collection.mapReduce(function () {
    Object.keys(this).map(function(key) {
        if (key.match(/^fk/)) emit(key, null);

        // OR: key.indexOf("fk") === 0
    });
}, function(/* key, values */) {
    // No need for params or to return anything in the 
    // reduce, just pass an empty function.
}, { out: { inline: 1 }});

Isso produzirá algo assim:
{
    "results": [{
        "_id": "fkKey1",
        "value": null
    }, {
        "_id": "fkKey2",
        "value": null
    }, {
        "_id": "fkKey3",
        "value": null
    }],
    "timeMillis": W,
    "counts": {
        "input": X,
        "emit": Y,
        "reduce": Z,
        "output": 3
    },
    "ok" : 1
}

Para obter nomes de campos e qualquer um ou todos (documento inteiro) seus valores:
db.test.mapReduce(function () {
    var obj = this;

    Object.keys(this).map(function(key) {
        // With `obj[key]` you will get the value of the field as well.
        // You can change `obj[key]` for:
        //  - `obj` to return the whole document.
        //  - `obj._id` (or any other field) to return its value.

        if (key.match(/^fk/)) emit(key, obj[key]);
    });
}, function(key, values) {
    // We can't return values or an array directly yet:

    return { values: values };
}, { out: { inline: 1 }});

Isso produzirá algo assim:
{
    "results": [{
        "_id": "fkKey1",
        "value": {
            "values": [1, 4, 6]
        }
    }, {
        "_id": "fkKey2",
        "value": {
            "values": ["foo", "bar"]
        }
    }],
    "timeMillis": W,
    "counts": {
        "input": X,
        "emit": Y,
        "reduce": Z,
        "output": 2
    },
    "ok" : 1
}

Para obter nomes de campos em subdocumentos (sem caminho):

Para fazer isso, você terá que usar store JavaScript functions on the Server :
db.system.js.save({ _id: "hasChildren", value: function(obj) {
    return typeof obj === "object";
}});

db.system.js.save({ _id: "getFields", value: function(doc) {
    Object.keys(doc).map(function(key) {
        if (key.match(/^fk/)) emit(key, null);

        if (hasChildren(doc[key])) getFields(doc[key])
    });
}});

E altere seu mapa para:
function () {
    getFields(this);
}

Agora execute db.loadServerScripts() para carregá-los.

Para obter nomes de campos em subdocumentos (com caminho):

A versão anterior retornará apenas os nomes dos campos, não o caminho inteiro para obtê-los, o que você precisará se o que deseja fazer é renomear essas chaves. Para obter o caminho:
db.system.js.save({ _id: "getFields", value: function(doc, prefix) {
    Object.keys(doc).map(function(key) {
        if (key.match(/^fk/)) emit(prefix + key, null);

        if (hasChildren(doc[key]))
            getFields(doc[key], prefix + key + '.')
    });
}});

E altere seu mapa para:
function () {
    getFields(this, '');
}

Para excluir correspondências de caminhos sobrepostos:

Observe que se você tiver um campo fkfoo.fkbar , ele retornará fkfoo e fkfoo.fkbar . Se você não quiser correspondências de caminho sobrepostas, então:
db.system.js.save({ _id: "getFields", value: function(doc, prefix) {
    Object.keys(doc).map(function(key) {
        if (hasChildren(doc[key]))
            getFields(doc[key], prefix + key + '.')
        else if (key.match(/^fk/)) emit(prefix + key, null);
    });
}});

Voltando à sua pergunta, renomeando esses campos:

Com esta última opção, você obtém todos os caminhos que incluem chaves que começam com fk , então você pode usar $rename por isso.

No entanto, $rename não funciona para aqueles que contêm arrays, então para aqueles você pode usar forEach para fazer a atualização. Consulte MongoDB renomear campo de banco de dados no array

Observação de desempenho:

MapReduce não é um pensamento particularmente rápido, então você pode querer especificar { out: "fk_fields"} para gerar os resultados em uma nova coleção chamada fk_fields e consulte esses resultados mais tarde, mas isso dependerá do seu caso de uso.

Possíveis otimizações para casos específicos (esquema consistente):

Além disso, observe que, se você sabe que o esquema de seus documentos é sempre o mesmo, basta verificar um deles para obter seus campos, para poder fazer isso adicionando limit: 1 para o objeto de opções ou apenas recuperando um documento com findOne e lendo seus campos no nível de aplicação.