MongoDB in my photostre.am

Time to blog about the reasons why I’ve migrated my photostre.am application from MySQL to MongoDB. I hope by this I can give a few people arguments to do the same.

Document based

photostre.am is powered by Ruby, a dynamic and fully object oriented programming language (well, except for blocks). MySQL isn’t that far off when using ruby, you can’t stack data rows into other rows but any decent database adapter will do those relations for you. But if you heart dynamic functionality as much as I do there’s no future for you in a schema database and the schema-less ones your best option.

Arrays

Arrays are a wonderful data structure to work with, e.g. for tags, but if you want to access single elements from an array in a mySQL database you most likely end up creating at least one more table, where you store each single element of your array, and query on that. I know, there are workarounds but MongoDB stores it’s data in BSON format which offers you exactly that in the straightmost way. I couldn’t be more happy to store my tags right inside a photo and retrieving photos that match a tag is only one query. For photostre.am it also allowed me to add new features, for example the Website model stored a domainhost and in the MySQL database I was too lazy for this second table as it wasn’t a much important feature but in MongoDB it has become very quick and easy to add with no loss in performance (and for an object that’s queried with every request that is important.

Embedded objects

This is one of the most amazing features and completes my object oriented programming experience. With MongoDB you can store whole objects inside a parent objects. For example the Website in photostream stores albums and pages which are of course models in my rails application and have all the functionality a regular model would have except the don’t need another collection (table in sql speak). The old database had more than 10 tables, the new one only three collections. It helped me to reduce the number of queries and it just feels natural.

No more SQL

I know SQL quite well. Learned it the hard way in the early 2000s and I always prefer the console over phpMyAdmin and similar. With MongoDB I have a great console, with all modern features and as a bonus it’s all javascript which is a great language and much undervalued by most people but as powerful as ruby.

Sharding

Not yet a feature that I need but photostre.am is close to 200 users now and they all come with around 1000 photos (respectively just the data about those photos) and the database must grow faster in the future if I want to make any money with it. MongoDB with it’s sharding will allow me to split the database horizontal over several small instances and I won’t have to upgrade a single DB server with more RAM and CPUs like you would need with a mySQL database.

don't bogart that block my friend

I just tried in our adva cms to use a render :update with a block. Quite simple thing and I’m surprised it haven’t been used anywhere else in the app. Even more surprising I got an error saying: tried to create Proc object without a block and googleing for it didn’t help at all, only a few people ran into the problem. But the stack revealed that the render call went through two plugins of ours and a quick look showed that those methods didn’t accept or pass the block that I gave render :update.

So kids, don’t forget to pass on the block.

Euruko 2008 - Day one

the conference started smoothly, despite not enough power cords (lucky me with my iPod touch) and the Prague marathon right outside the university. The keynote was hold by Matz and reeally grew in the second half where it was about m17n and issues with that. Koichi showed some benchmarks and details from his YARV. Charles and Thomas from the JRuby announced the brandnew version 1.1 and outlined it's new features and optimisations. They'll second part was writing GUI apps, a five-liner for a swing-frame including a button and an event many LOC less than pure Java. The Rail part was kept short and featured a few references of production sites running JRuby. After a short break to their mac's problems and questions from the audience about testing the ruby-specs, the finished with demoing ruby-processing (this great graphics library) samples running them on JRuby.

Euruko 2008

It's only a day and I'll be sitting in the trains (seven hours) to Prague for the "Euruko 2008":http://www.euruko2008.org/ where Matz will hold the keynote and the continent's ruby hackers will gather for two days. Things I'm really looking forward for: * living without my Macbook for a whole weekend * David A. Black on „Per-Object Behavior in Ruby“ * Nic Williams — „Meta-Meta-Programming with Ruby“ * Tim Becker — „Lessons Learned Writing Native Extensions“ * all those lightning talks * and of course the parties on Friday and Saturday

Google chart

I stumbled accross (well, let’s say it was on delicious popular) about gchartrb which is a Ruby API for Google Charts and the only place to work it into was my good old ananasblau.de

The examples from the API are quite simple and when I tried to do a chart for the last twenty days I was in big trouble. Here’s my source which uses some rarely used Date methods and by far to many collects. Basically I get my models data, create a hash with date-keys, fill it up, sort it (afterwards it’s an array for some reason) and for the two axis I collect and max on the array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

stats = MyModel.find(:all, :group => 'date', :order => 'created_at ASC',
    :select => ['count(id) as count, DATE_FORMAT(created_at, "%Y-%m-%d") as date'], 
    :conditions => ' created_at > from_unixtime(unix_timestamp() - 3600*24*21)')
  unless stats.empty?
    @dates = {}
    Date.parse(stats[0].date).upto(Date.today()){|d| @dates[d.strftime('%Y-%m-%d')] = 0 }
    stats.collect { |c| @dates[c.date.to_s] = c.count.to_i }
    @dates = @dates.sort{|a,b| a[0] <=> b[0]} # note, it's an array from here on
    #  d[0][8,2] for day only. d[0][5,5] for month and day
    @chart =  GoogleChart::BarChart.new('700x100', "", :vertical, true)
    @chart.axis :x, :labels => @dates.collect{|d| d[0][8,2] }, :alignment => :left
    @chart.axis :y, :range => [0, @dates.max{|a,b| a[1] <=> b[1]}[1]]
    @chart.grid
    @chart.data "", @dates.collect{|d| d[1]}, '55CC88' 
  end

Deutschsprachige Monate in Ruby

Nicht gerade neu der Tipp, aber ich brauchte einfach nur die deutschen (bzw. österreichischen) Monatsnamen und schneller als so kann’s nicht gehen:

1
2
Date::MONTHNAMES = [nil] + %w(Jänner Feber März April Mai Juni Juli \
August September Oktober November Dezember)

Das ist doch immer wieder das schöne an Ruby, nur weil’s ne Klasse oder eine Konstante ist heißt das nicht dass man nicht hinterher nochmal was dran ändern könnte. Einfach herrlich bequem.