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
Euruko 2008
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.