- An introduction to CouchDB
- Installing CouchDB
- Experimenting with CouchDB’s web interface
- Integrating with Rails using ActiveCouch
- Integrating with Rails using RelaxDB
- Getting to scaffolding using RelaxDB
- Installing CouchDB from Subversion source code
- 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.
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
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); }
Image may be NSFW.
Clik here to view.
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?! :)
Image may be NSFW.
Clik here to view.
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 }); } }
Image may be NSFW.
Clik here to 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.
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
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
Image may be NSFW.
Clik here to view.
Clik here to view.
Clik here to view.
Clik here to view.
