We've just launched our site - Magnifeast (http://www.magnifeast.com) - which uses CouchDB as its primary persistence mechanism.
Magnifeast is a site that lets you order online from over 500 restaurants. We're initially launching in Los Angeles, but plan to expand to other cities soon. CouchDB was a good fit for us because restaurant and menu data can be very complicated - and since we're modeling real world data, it doesn't fit any kind of rigid schema. Even something as simple as delivery area can be quite variable - some restaurants don't deliver at all, others deliver to a radius, some have multiple polygonal delivery regions (each with different fees), some deliver to a list of cities, etc. And menus are even more complicated. Having a traditional schema-oriented database (SQL) would be incredibly frustrating and limiting. We've been able to use CouchDB's flexibility to express a much richer understanding of restaurants and menus than pretty much anyone else out there. Some technical notes: Our site runs on Amazon EC2. Clients don't talk to CouchDB directly - they talk through our application servers (currently running Merb). The app servers do authorization for reads - for example, we have custom validation rules for various views that are addressable by the client. We have validation rules that run in CouchDB for writes. Right now, since there isn't a 1-to-1 mapping between our site users and CouchDB users, we actually append some additional fields to each document at write-time containing information about the user role and permissions (ideally, this could be passed as an additional parameter to CouchDB, which could be checked in the validation function). The app servers also pre-save hooks that match various criteria, and might modify the documents before saving. It turns out that to for this to work correctly in our case, we actually have to return a list of deltas (fields that have been changed / removed) to the client browser so they can be applied. Returning the full object to the client doesn't work because the client may have made changes to the object since the write but before the write finished. This is a difficult problem to solve in general, caveat emptor. Unlike many traditional websites, and more akin to a couchApp, the actual HTML pages for our site are constructed on the in-browser on the client. The clients download templates from the server, along with JSON data that comes from CouchDB, and use the data with the template to assemble the page. The templates are written in a proprietary language called Jolt, which is javascript-based and 'Live' - in the sense that changes in the JSON data update the page in realtime. Some of the pages on the site have a very interactive feel because of this. When the client loads data from CouchDB, the objects are loaded using a system that can transform the JSON into instances of Javascript classes, resolving references to other CouchDB documents (and references to members of other document). For example, when loading a menu, we typically load a menu document, menu items, and menu sections, all which are stored as top-level CouchDB documents. These documents all reference each other, and come in the run-time as instantiated javascript classes, with all of the references fixed up. A menu item can consist of dozens of internal objects as well. If changes were made to the menu item (for example, in our menu editing admin tools), the menu item would be serialized back out to JSON, all of the references to external objects replaced with pointer stubs, and written to CouchDB. This system effectively lets us serialize out fairly complicated object graphs into CouchDB. Our search page (http://www.magnifeast.com/restaurant) doesn't hit CouchDB. Instead, we have a custom search server, which pulls documents from CouchDB using all_docs_by_seq, and does custom indexing. The searching effectively buckets couchdb documents by various criteria, and finds the intersection of the various buckets in responses to queries (for example, you might search for restaurants open 'now' that deliver to your home address with Thai cuisine that contain the search term 'Bangkok'). For textual searches, the search server hits Ferret (similar to Lucene), and intersects the returned result list with the other criteria. There's a lot more going on behind the scenes. We actually have a lot more admin pages than regular pages - building tools so we could efficiently get hundreds of menus and restaurants fully represented in our system was a lot of work. And this is just the beginning - we've been focusing intensely on online ordering for launch, but want to build out the site to be much more than that. I can go into more technical details if people are interested. Thanks for all your hard work so far - I'm definitely glad we had an alternative to SQL! Scott
