sql >> Base de Datos >  >> NoSQL >> MongoDB

Consulta y filtra nombres de claves en lugar de valores en MongoDB

Puede hacerlo usando mapReduce :

Para obtener solo los nombres de campo en el nivel raíz:

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

Esto generará algo como esto:

{
    "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 obtener nombres de campo y alguno o todos (documento completo) sus 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 }});

Esto generará algo como esto:

{
    "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 obtener nombres de campo en subdocumentos (sin ruta):

Para hacer eso tendrás 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])
    });
}});

Y cambia tu mapa a:

function () {
    getFields(this);
}

Ahora ejecuta db.loadServerScripts() para cargarlos.

Para obtener nombres de campo en subdocumentos (con ruta):

La versión anterior solo devolverá los nombres de los campos, no la ruta completa para obtenerlos, que necesitará si lo que quiere hacer es cambiar el nombre de esas claves. Para obtener la ruta:

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

Y cambia tu mapa a:

function () {
    getFields(this, '');
}

Para excluir coincidencias de rutas superpuestas:

Tenga en cuenta que si tiene un campo fkfoo.fkbar , devolverá fkfoo y fkfoo.fkbar . Si no desea coincidencias de rutas superpuestas, entonces:

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

Volviendo a su pregunta, cambiando el nombre de esos campos:

Con esta última opción, obtienes todas las rutas que incluyen claves que comienzan con fk , por lo que puede usar $rename por eso.

Sin embargo, $rename no funciona para aquellos que contienen matrices, por lo que podría usar forEach para hacer la actualización. Consulte Campo de base de datos de cambio de nombre de MongoDB dentro de la matriz

Nota de rendimiento:

MapReduce no es particularmente rápido, por lo que es posible que desee especificar { out: "fk_fields"} para enviar los resultados a una nueva colección llamada fk_fields y consulta esos resultados más tarde, pero eso dependerá de tu caso de uso.

Posibles optimizaciones para casos específicos (esquema consistente):

Además, tenga en cuenta que si sabe que el esquema de sus documentos es siempre el mismo, solo necesita marcar uno de ellos para obtener sus campos, por lo que puede hacerlo agregando limit: 1 al objeto de opciones o simplemente recuperando un documento con findOne y leyendo sus campos en el nivel de aplicación.