CouchDB on Rails (part 3 of ?)

  1. An introduction to CouchDB
  2. Installing CouchDB
  3. Experimenting with CouchDB’s web interface
  4. Integrating with Rails using ActiveCouch
  5. Integrating with Rails using RelaxDB
  6. Getting to scaffolding using RelaxDB
  7. Installing CouchDB from Subversion source code
  8. Trying out couchrest and topfunky’s basic_model

Time for a little play with the CouchDB. Let’s see what this thing can do!

The first thing i notice is some test databases left over from when i ran the test suite. I’m not sure if that’s supposed to happen. I kind of expected the tests to clean up after themselves. But anyway, it means i can have a little look and see what data they contain.

CouchDB test data

Apparently nothing very much. We have some documents with keys and a ‘rev’ value, whatever that might be. Looking inside a document, it contains some examples of an integer field and a string field. I could delete them and add my own fields, if i wish. But this is not so interesting. I’m going to create my own database!

My CD collection

Here’s a fantastic opportunity to totally embarrass myself by revealing the CDs i own. I have previously made a CD collection database in Lotus Notes, so i think it will work. I’ll use the ‘Create Database’ option in the web interface and call it ‘cd_collection’. The log informs me what has happened:

[info] [] 127.0.0.1 - - "GET /_utils/browse/_create_database.html" 304
[info] [] 127.0.0.1 - - "PUT /cd_collection/" 201
[info] [] 127.0.0.1 - - "GET /_utils/browse/database.html" 200
[info] [] 127.0.0.1 - - "GET /cd_collection/_all_docs" 200

So now i’ll go ahead and start making documents. Hmm, what should i use as the ID, i wonder? Presumably anything that uniquely identifies the CD. But since i’m going to be using this with Rails, i’ll stick to the convention of an auto_increment integer. So my first CD is going to be ID 1.

Edit to add: You don’t need to choose an ID yourself. You can leave it blank and press Create, then it generates a UUID for you.

Well this is fun. I can create all the fields i want. The beauty of CouchDB being a document-oriented database is i can make up the field names as i go along – i am not constrained by a schema.

Entering data into CouchDB

When i’m ready i’ll go ahead and click Save Document and see what happens in the log.

[info] [] 127.0.0.1 - - "PUT /cd_collection/1" 201
[info] [] 127.0.0.1 - - "GET /_utils/browse/document.html" 200
[info] [] 127.0.0.1 - - "GET /cd_collection/1" 200
[info] [] 127.0.0.1 - - "PUT /cd_collection/1" 201
[info] [] 127.0.0.1 - - "GET /cd_collection/1" 200

Okay, not very interesting, and i’ve even stripped out some of it. I guess it created a document, showed it to me, accepted my changes and showed it to me again.

I notice in the web interface that there are two versions of the document: one with just the _id and _rev fields, and a newer one with the fields i added. Version control is apparently built into CouchDB! Very good!

Views

So i have created a few documents; now i should be able to view them. The default CouchDB view is “All documents” but it seems i could create my own custom view if i knew how. I have to write some sort of map function to apply to each document. Here’s an idea:

function(doc) {
  emit(doc.title, doc);
}

Creating a view in CouchDB

Now that is exciting! It has output the title in the column on the left, and it has also ordered by title. I’m going to save that as a ‘by_title’ view. Oops, there’s a gotcha. It doesn’t seem to like underscores in IDs. So i’ll just save it as ‘title’. Now it shows up under Design documents. It’s just another document in the database! How nifty is that?! :)

CouchDB view stored as a design document

Now i’ll try to create another view. For some reason i’d like to see all my CDs which were released after 2003, ordered by artist. I suppose i’ll need a map and a reduce to do that.

Oh cool, just as i am writing this, @benatkin tweets up a link to the views on Divan, which include some reduce functions. This is collective consciousness at its best! :)

It seems that this reduce function produces a unique list, removing duplicates …

function(keys, values) {
  return sum(values);
}

… but how to filter down by year?

Ahhh, got it! You don’t need the reduce function to filter out documents, you just put an ‘if’ clause in the map. It’s JavaScript, FTW! :D I also figured out how to tweak the output a bit …

function(doc) {
  if(doc.year_of_release > 2003) {
    emit([doc.artist, doc.title], {
      artist: doc.artist,
      year: doc.year_of_release,
      publisher: doc.publisher
    });
  }
}

CouchDB - a more detailed view

Good job! :) Now i’m not really sure what the reduce function is for … maybe that’s a question for discussion in the comments.

One more thing i gleaned from looking at the Divan views … it is possible to store multiple views in a single design document. So i can create a document called _design/cds and create by_title and by_artist within it. This probably makes more sense.

Views into CouchDB

Tracks

In a relational database we would store tracks of a CD in a different table, with a foreign key linking it to the CD. Rails would understand the two tables as two models:

class Cd < ActiveRecord::Base
  has_many :tracks
end

class Track < ActiveRecord::Base
  belongs_to :cd
end

With a document-oriented database, such as CouchDB, we’ll presumably do this a bit differently. JSON, like XML can be nested, so perhaps a CD and its tracks may be represented in JSON like this:

{
  "title": "Screaming Serenades",
  "artist": "Kindle",
  "year_of_release": 2002,
  "publisher": "Alliance Music"
  "tracks": [
    {"title": "Every Little Thing You Do", "length": "3:25"},
    {"title": "Don't Fly Away", "length": "4:16"},
    {"title": "Brighter Days", "length": "3:08"},
    {"title": "Little Bit Of Your Love", "length": "3:59"},
    {"title": "Days Like These", "length": "3:42"},
    {"title": "Gone Crazy", "length": "3:22"},
    {"title": "Live For Heaven", "length": "4:36"},
    {"title": "Someone To Live For", "length": "3:39"},
    {"title": "Step On Up", "length": "4:31"},
    {"title": "Not Impossible", "length": "3:10"},
    {"title": "Here I Stay", "length": "4:32"},
    {"title": "State Of Waiting", "length": "6:54"}
  ]
}

Edit to add: There are some discussions about the best way to join data; nested or in separate documents. In this case i think nested is good because the tracks really help to define the CD. In the case of a blog post with comments, the comments could easily stand alone as separate documents.

Certainly the web interface accepts this format, and i have managed to store 12 tracks into my document.

CouchDB with nested data

I could really do with some confirmation that i’m doing the right thing with this nested data. I’m not quite sure how we’ll deal with this when we get to Rails.

I think that will be the challenge for my next installment! :)

Part 4: Integrating with Rails using ActiveCouch

Advertisements

17 comments on “CouchDB on Rails (part 3 of ?)

  1. Pingback: Leah Culver » Development Environment

  2. how to install couchdb on windows and any examples for couchdb-java-lucene integration

  3. Nice screen shots.. The code which is given here is very useful for me.. Keep writing up these all and make the blog most viewed one..

  4. So now i'll go ahead and start making documents. Hmm, what should i use as the ID, i wonder? Presumably anything that uniquely identifies the CD. But since i'm going to be using this with Rails, i'll stick to the convention of an auto_increment intege

  5. Hi aimee,can you please guide me how can I install CouchDB on windows. I am not getting it.ThanksRegards.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s