views:

578

answers:

4

What can I do to share code among views in CouchDB?

Example 1 -- utility methods

Jesse Hallett has some good utility methods, including

function dot(attr) {
  return function(obj) {
      return obj[attr];
  }
}

Array.prototype.map = function(func) {
  var i, r = [],
  for (i = 0; i < this.length; i += 1) {
    r[i] = func(this[i]);
  }
  return r;
};

...

Where can I put this code so every view can access it?

Example 2 -- constants

Similarly for constants I use in my application. Where do I put

MyApp = {
  A_CONSTANT = "...";
  ANOTHER_CONSTANT = "...";
};

Example 3 -- filter of a filter:

What if I want a one view that filters by "is this a rich person?":

function(doc) {
  if (doc.type == 'person' && doc.net_worth > 1000000) {
    emit(doc.id, doc);
  }
}

and another that indexes by last name:

function(doc) {
  if (doc.last_name) {
    emit(doc.last_name, doc);
  }
}

How can I combine them into a "rich people by last name" view?

I sort of want the equivalent of the Ruby

my_array.select { |x| x.person? }.select { |x| x.net_worth > 1,000,000 }.map { |x| [x.last_name, x] }

How can I be DRYer?

A: 

You can't do this (last I checked) because the views are stored in the database, and the key for the view is a hash of itself. A view cannot rely on outside data/logic/programming, because if it changes then the view is different and won't match. It confused me, and still does, so I may be wrong.

Mike Trpcic
Since couchdb has to update the indexes when a view changes it needs to know when the view has changed. If you rely on external libraries or code in the view then the view can change without couchdb's knowledge. This will cause the index to be stale and possibly incorrect. That being said you could theoretically create your own javascript view server that bundles the libraries with the caveat and risks noted above.
Jeremy Wall
+4  A: 

From the CouchDB Wiki:

There are no development plans to share code/functions between views. Each view function is stored according to a hash of their byte representation, so it is important that a function does not load any additional code, changing its behavior without changing its byte-string. Hence the use-case for CouchApp.

kanngard
A good partial answer -- any idea about chaining views?
James A. Rosen
+2  A: 

The answer lies in couchapp. With couchapp you can embed macros that include common library code into any of the design document sections. It is done before the design document is submitted to the server. What you need to do to do the query you ask about is reverse the keys that are emitted so you can do a range query on the "network"

function(doc) 
{
  if (doc.type == 'person') 
  {
    emit([doc.net_worth, doc.lastname], null);
  }
}

You don't want to include the doc you can do that with include_docs=true on the query parameters. And you get the doc.id for free as part of the key. Now you can do a range query on networth which would look something like this.

http://localhost:5984/database/_design/people/_view/by_net_worth?startkey=[1000000]&amp;endkey=[{},{}]&amp;include_docs=true
fuzzy lollipop
+1  A: 

Couchapp will "macro" in libraries, and it works pretty well.

The other, unsupported option is to add utility functions like that to a custom query server. The JS file is not that difficult to understand, and the Ruby and Python versions are even simpler. The view server compiles the strings in the design doc into function objects as they are executed, so if you close those functions over utility functions, constants or whatever, they'll be executable in map/reduce/show/list functions.

Look for the place in the main.js file where "emit" and "log" are defined, and emulate the definition of those functions to expose your custom utility functions to your map and reduce lambdas.

Caveat: Changing the view server without requiring a rebuild on your view will mean that your view index will not be correct. Programmer Beware.

Stephen