What’s New?
- Pagination! CouchDB offers great support for retrieving a subset of data, but the housekeeping is tricky. RelaxDB takes care of it.
- Note that if you invoke paginate_by on an already created view, the necessary reduce function won’t be automatically created. Take a look at SortedByView and create the reduce func by hand.
- Support for multi key post
- For example, @ Numbers.all.sorted_by(:val) { |q| q.keys([1,2,3,5]) } @
- Works with CouchDB 0.9 trunk as of 2008/10/08. Note that pagination won’t work correctly on trunk until issue COUCHDB-135 is fixed.
Note: 0.2.1 requires CouchDB 0.9 trunk. 0.2.0 works with CouchDB 0.8 onwards.
Overview
RelaxDB provides a Ruby interface to CouchDB. It offers a simple idiom for specifying object relationships. The underlying objects are persisted to the mighty CouchDB. Combined with the schema free nature of CouchDB, RelaxDB’s current strength lies in quick prototyping of object models.
A few facilities are provided including pretty printing of GET requests and uploading of JavaScript views.
A basic merb plugin, merb_relaxdb is also available.
For more complete documentation take a look at docs/spec_results.html and the corresponding specs.
Details
Getting started
RelaxDB.configure :host => "localhost", :port => 5984
RelaxDB.use_db "scratch"
Defining models
class Writer < RelaxDB::Document
property :name, :default => "anon"
has_many :posts, :class => "Post"
has_many :ratings, :class => "Post", :known_as => :critic
end
class Post < RelaxDB::Document
property :created_at
property :contents
belongs_to :writer
has_many :ratings, :class => "Rating"
end
class Rating < RelaxDB::Document
property :thumbs_up, :validator => lambda { |tu| tu >= 0 && tu < 3 }, :validation_msg => "No no"
belongs_to :post
belongs_to :critic
end
Exploring models
paul = Writer.new(:name => "paul").save
post = Post.new(:contents => "foo")
paul.posts << post # post writer is set and post is saved
post.created_at # right now
paul. << Rating.new(:thumbs_up => 3, :post => post) # returns false as rating fails validation
paul..size # 0
# Simple views are auto created
Rating.all.sorted_by(:thumbs_up) { |q| q.key(2).count(1) } # query params map directly to CouchDB
Paginating models
# Controller (merb-action-args used for extracting view_params)
def action(page_params={})
u_id = @user._id
@posts = Post.paginate_by(page_params, :writer_id, :created_at) do |p|
p.startkey([u_id, {}]).endkey([u_id]).descending(true).count(5)
end
render
end
# In your view
<% @posts.each do |p| %>
<%= p.contents %>
<% end %>
<%= link_to "prev", "/posts/?#{@posts.prev_query}" if @posts.prev_query %>
<%= link_to "next", "/posts/?#{@posts.next_query}" if @posts.next_query %>
Paginating over your own views
RelaxDB.paginate_view(page_params, "Letter", "by_letter_and_number", :letter, :number) do |p|
p.startkey(["b"]).endkey(["b", {}]).count(2)
end
A more illustrative example is listed in the .paginate_view spec in spec/paginate_spec.rb
Creating views by hand
$ cat view.js
function Writer-allnames-map(doc) {
if(doc.class == "Writer")
emit(null, doc.name);
}
function Writer-allnames-reduce(keys, values) {
var allnames = "";
for(var i = 0; i < values.length; i++)
allnames += values[i];
return allnames;
}
$
RelaxDB::ViewUploader.upload("view.js")
RelaxDB.view("Writer", "allnames") # paul
Visualise
Create an object graph by simply running
RelaxDB::GraphCreator.create
Requires graphviz. Useful for visualising relationships between a limited number of document e.g. test fixtures. Description and example.
Experimental Features
- Declarative denormalisation
- Create a partial object graph in JSON with a single call
- May be used to require fewer GET requests
- View the denormalisation spec for examples
Incomplete list of limitations
- Error handling is not robust
- Destroying an object results in non transactional nullification of child/peer references
- Objects can talk to only one database at a time
- No caching is used. Although adding an LRU cache would be fairly straightforward, this hasn’t been done as it’s not yet clear what caching strategies will be most effective.