sql >> Database >  >> NoSQL >> MongoDB

Zoek en filter sleutelnamen in plaats van waarden in MongoDB

U kunt dat doen met mapReduce :

Om alleen de veldnamen op hoofdniveau te krijgen:

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

Dit zal zoiets als dit opleveren:

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

Om veldnamen en een of alle (hele document) waarden te krijgen:

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

Dit zal zoiets als dit opleveren:

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

Om veldnamen in subdocumenten te krijgen (zonder pad):

Om dat te doen, moet je 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])
    });
}});

En verander je kaart in:

function () {
    getFields(this);
}

Voer nu db.loadServerScripts() uit om ze te laden.

Om veldnamen in subdocumenten te krijgen (met pad):

De vorige versie retourneert alleen veldnamen, niet het hele pad om ze te krijgen, wat je nodig hebt als je die sleutels wilt hernoemen. Om het pad te krijgen:

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

En verander je kaart in:

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

Overlappende padovereenkomsten uitsluiten:

Merk op dat als je een veld fkfoo.fkbar . hebt , zal het fkfoo . teruggeven en fkfoo.fkbar . Als u geen overlappende padovereenkomsten wilt, dan:

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

Teruggaand op uw vraag, hernoemen van die velden:

Met deze laatste optie krijg je alle paden die sleutels bevatten die beginnen met fk , zodat u $rename kunt gebruiken daarvoor.

Echter, $rename werkt niet voor degenen die arrays bevatten, dus voor degenen die u zou kunnen gebruiken forEach om de update uit te voeren. Zie MongoDB hernoem databaseveld binnen array

Prestatienotitie:

MapReduce is niet bijzonder snel bedacht, dus misschien wilt u { out: "fk_fields"} specificeren om de resultaten uit te voeren in een nieuwe verzameling genaamd fk_fields en vraag die resultaten later op, maar dat hangt af van uw gebruik.

Mogelijke optimalisaties voor specifieke gevallen (consistent schema):

Houd er ook rekening mee dat als u weet dat het schema van uw documenten altijd hetzelfde is, u slechts één van hen hoeft aan te vinken om de velden te krijgen, zodat u dat kunt doen door limit: 1 toe te voegen. naar het options-object of gewoon één document ophalen met findOne en het lezen van de velden op applicatieniveau.



  1. SignalR schalen met Azure Worker Role en OWIN

  2. Mongo db-array verandert string in float

  3. Kan sharded mongoDB niet upgraden of de balancer stoppen

  4. Moet ik de Redis-verbinding tussen bestanden/modules delen?