Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CouchDB: Adding fields to "doc" in view

In my view I want to create a new object to return instead of the object that is passed to my map function. I would like to do some validation and add some fields to the new object. My map function for the view looks like this (there isn't a reduce):

function(doc) {
    if(doc.type == "mytype") {
        var newobj = null;
        if( doc.someObjField ) {
            newobj = doc.someObjField;  //LINE 5: want a copy but get a reference
        }
        else {
            newobj = new Object();      //field didn't exist create a new object
        }

        newobj.superId = doc._id;       //these fields get added to the DB
        newobj.superName = doc.name;
        newobj.newField = doc.field;

        emit(doc._id, newobj);
    }
}

The problem is that when this view runs, it ends up adding some new fields to the documents in the database. I cannot see the fields when editing the doc in Futon, but they show up in every view of the document.

I think this is because because of LINE 5 creates a reference to that field of the document instead of a copy. Does this mean I have to include a clone() function in all my views to get what I want? Maybe I'm thinking about this wrong?

Update: I was using CouchDB version 1.0.0 and upgraded to version 1.0.1 and it seems that the behavior has changed. In the previous version when "doc" was modified in one view, the modification appeared in all views. In the newest version, this does not seem to be the case.

like image 396
Dusty Campbell Avatar asked Jul 30 '26 02:07

Dusty Campbell


1 Answers

newobj = doc.someObjField does indeed make a reference and not a copy, so you'll will have to iterate over the properties of doc.someObjField and copy them over to a new object.

This is not that trivial, the following shows an example of how to make a shallow clone:

// get the class of an object, this is a lot better than messing with typeof and instanceof
function is(type, obj) {
    return Object.prototype.toString.call(obj).slice(8, -1) === type;
}

function copy(val) {
    if (is('Object', val)) { // shallow clone objects
        var obj = {};
        for (var f in val) {
            if (val.hasOwnProperty(f)) {
                obj[f] = val[f];
            }
        }
        return obj;

    } else {
        return is('Array', val) ? val.slice() : val; // shallow clone arrays
    }
}

Your code would look like this:

newobj = doc.someObjField ? copy(doc.someObjField) : {};

Now if you someObjField has objects or arrays as its properties those will yet again only be references, so you have to recursively clone them.

like image 81
Ivo Wetzel Avatar answered Jul 31 '26 16:07

Ivo Wetzel