3

I am using Cradle to store objects in CouchDB from my Node.js server. The objects contain functions....

function AnObject(a, b){
this.a = a; this.b = b;
this.addparts = function(){return this.a + this.b;};}

var cradle = require('cradle');
var db = new(cradle.Connection)('http://localhost', 5984, {cache: true, raw: false}).database('myDB');

var myObject = new AnObject(1, 2);
console.log("addparts:" + myObject.addparts());
db.save('myObjectId', myObject);

This works fine and the document is stored but when I retrieve it, I can no longer call the function on the returned document...

db.get('myObjectId', function(err, myRetrievedObject){
console.log("addparts:" + myRetrievedObject.addparts());
});

This fails with a (Property is not a function) Error..

node cradle_test

cradle_test.js:21
console.log("addparts:" + myRetrievedObject.addparts());
                                         ^
TypeError: Property 'addparts' of object {"_id":"myObjectId","_rev":"2-83535db5101fedfe30a1548fa2641f29","a":1,"b":2,"addparts":"function (){return this.a + this.b;}"} is not a function
Michael Dausmann
  • 4,202
  • 3
  • 35
  • 48
  • I would be interesting to have a module that would save function references to the db and you could call them on retrieving them. Something like dnode does it. – fent Jan 27 '12 at 01:44

2 Answers2

2

CouchDB stores JSON. Functions are not valid JSON. Functions are never stored in the database.

I recommend you move the functions out into a prototype.

function AnObject(a, b){
    this.a = a; this.b = b;
}

AnObject.prototype.addparts = function(){
    return this.a + this.b;
};

db.get('myObjectId', function(err, myRetrievedObject){
    var obj = Object.create(AnObject.prototype);
    // for some value of extend ( https://github.com/Raynos/pd#pd.extend )
    extend(obj, myRetrievedObject);
    console.log("addparts:" + obj.addparts());
});

This way your not saving functions and you can still operate on your object using your methods. You just have to make sure that your retrieved object is made an instance of AnObject

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • Thanks Ray, that is the answer, unfortunately I couldn't get your module to work so I ended up downloading and ./ requiring this file instead to get the 'extend' function... https://raw.github.com/DracoBlue/spludo/master/core/util.js – Michael Dausmann Jan 26 '12 at 21:38
  • @MichaelDausmann could you highlight what part of it doesn't work? I'll fix it if you open a bug report on pd. – Raynos Jan 26 '12 at 22:42
0

There is a way to store functions in CouchDB: as attachments.

Define your functions in a separate .js file (for example, a set of functions you want to share across multiple servers or app instances).

/modules/db.js:

var db = {}

db.insert = function(nanoDb, object, cb){
  //insert new doc
  nanoDb.insert(object, function(err, body, header) {
    if (!err) {
      cb.call(null, {success: true, data: body});

    }else{
      console.log('[insert] ', err.message); 
      cb.call(null, {success: false, error: err.message});
    }
  });
}

db.bulkInsert = function(nanoDb, array, cb){
  //structure for bulk insert
  var data = {
    docs: array
  }
  //insert new doc
  nanoDb.bulk(data, function(err, body, header) {
    if (!err) {
      cb.call(null, {success: true, data: body});

    }else{
      console.log('[bulkInsert] ', err.message); 
      cb.call(null, {success: false, error: err.message});
    }
  });
}

db.bulkDelete = function(nanoDb, array, cb){
  for(i in array){
    array[i]._deleted = true;
  }
  var data = {
    docs: array
  }
  //check if the url exists in the db
  nanoDb.bulk(data, function(err, body) {
    if (!err){  
      cb.call(null, {success: true, data: data});
    }else{
      console.log('[bulkDelete] ', err.message);
      cb.call(null, {success: false, error: err.message});
    }
  });
}

db.view = function(nanoDb, design, view, params, cb){
  nanoDb.view(design, view, params, function(err, body) {
    if (!err){
      var docs = util.extractDocs(body);
      cb.call(null, {success: true, data: docs});
    }else{
      console.log('[view] ', err.message);
      cb.call(null, {success: false, error: err.message});
    }
  });
}

db.search = function(nanoDb, design, index, params, cb){
  nanoDb.search(design, index, params, function(err, body) {
    if (!err) {
      var docs = util.extractDocsSearch(body);
      cb.call(null, {success: true, data: docs});
    }else{
      console.log('[search] ', err.message);
      cb.call(null, {success: false, error: err.message});
    }
  }); 
}

db.follow = function(nanoDb, params){
  var feed = nanoDb.follow(params);
  return feed;
}

module.exports = db;

Use a CouchApp to deploy the functions as attachments (in a design doc):

//your couchapp
var couchapp = require('couchapp')

//url to your database
var url = '...';

//empty design doc (for attachments)
ddoc = {
  _id: '_design/mods'
};

//folder containing .js files
couchapp.loadAttachments(ddoc, './modules/');

//this function uploads your attachments
couchapp.createApp(ddoc, url, function(app) {
  app.push(function(){
    //do something
  });
});

Now, get the functions wherever you need them:

//use mikaels request module if you like
var request = require('request');

//tell the app where to get your .js file
//sometimes a good idea to persist these references in memory or even in your couchdb
var fileUrl = '/_design/modules/db.js'

//set this variable in the proper scope
var db;

//we'll use this to 'require' the .js file
var _require = function(src, file) {
  var m = new module.constructor();
  m.paths = module.paths;
  m._compile(src, file);
  return m.exports;
}

request({ url: fileUrl, json: true }, function (err, response, data) {
  if (!err && response.statusCode === 200) {
    //now we assign our required .js file (object w/functions) back into usable form
    //woot woot!
    db = _require(data);
  }else{
    console.log('[request]', err);
  }
});

Do stuff!

db.doSomething()
Dave Romero
  • 128
  • 6