How do I DRY up my CouchDB views?

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?

Answers:

Answer

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]&endkey=[{},{}]&include_docs=true
Answer

As per this blog post, you can add commonjs modules to the map function (but not the reduce function) in views in couchdb 1.1 by having a key called lib in your views object. A lot of popular javascript libraries like underscore.js adhere to the commonjs standard, so you can use them in your views by using require("views/lib/[your module name]").

Say you include underscore.js as "underscore" in the lib object in views, like so:

views: {
    lib: {
         underscore: "// Underscore.js 1.1.6\n ...
    }
    ...
    [ the rest of your views go here]
}

, you can then add the following to your view to get access to the _ module:

var _ = require("views/lib/underscore");

For custom libraries, all you need to do is make anything you want to share in your library a value to the global "exports" object.

Answer

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.

Answer

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.

Answer

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.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.