Saturday, September 12, 2009

Prototyping Alternatives

‹prev | My Chain | next›

I spend a good deal of time today messing about with the CouchDB view for alternate recipe preparations. I will have documents describing these alternate preparations of the form:
{
"_id": "d16f491629b78a5bb13adb49ec6bee00",
"_rev": "2-4002233925",
"name": "tomato_sauce",
"type": "Alternative",
"recipes": [
"2002-08-12-sauce",
"2003-01-17-sausage_pasta",
"2003-02-13-pasta",
"2003-02-16-sauce",
"2003-04-07-cavatelli",
"2004-02-04-squid",
"2004-04-26-sauce",
"2006-02-14-sauce"
]
}
That is a real document. My wife's family is Italian and they have many, many different types of sauce. I am a little surprised that there is not one with a potato in it—something to add to the cookbook another day.

At any rate, I would like a view for alternate preparations such that I can lookup a recipe by ID and see a list of all alternate preparations for that recipe, excluding itself. I eventually work out this view:
function(doc) {
if (doc['type'] == 'Alternative') {
for (var i=0; i < doc['recipes'].length; i++) {
var alternatives = [];
for (var j=0; j < doc['recipes'].length; j++) {
if (i != j) {
alternatives.push(doc['recipes'][j]);
}
}
emit(doc['recipes'][i], alternatives);
}
}
}
That is not too complex a Javascript function, my main difficulty came when I omitted a closing parenthesis and got this unhelpful error:
cstrom@jaynestown:~/repos/eee-code$ curl http://localhost:5984/eee/_design/recipes/_view/alternatives\?key=\"2004-02-04-squid\"
{"error":"case_clause","reason":"{{bad_return_value,{os_process_error,{exit_status,0}}},\n
{gen_server,call,\n
[<0.12199.3>,\n
{prompt,[<<\"add_fun\">>,\n
<<\"function(doc) {\\n if (doc['type'] == 'Alternative') {\\n for (var i=0; i < doc['recipes'].length; i++) {\\n var alternatives = [];\\n for (var j=0; j < doc['recipes'].length; j++) {\\n if (i != j) {\\n alternatives.push(doc['recipes'][j];\\n }\\n }\\n emit(doc['recipes'][i], alternatives);\\n }\\n }\\n}\\n\">>]},\n
infinity]}}"}
"infinity"? Really?

The lesson that I take away from this is that, as easy as the couch_docs gem makes it to upload design documents, it has no Javascript error checking. I either need to add it (and a javascript testing framework while I am at it) or do my initial view building in the futon web interface.

With working view in hand, I can try it out from the command line:
cstrom@jaynestown:~/repos/eee-code$ curl \
http://localhost:5984/eee/_design/recipes/_view/alternatives\?key=\"2004-02-04-squid\"
{"total_rows":8,"offset":5,"rows":[
{"id":"d16f491629b78a5bb13adb49ec6bee00",
"key":"2004-02-04-squid",
"value":["2002-08-12-sauce",
"2003-01-17-sausage_pasta",
"2003-02-13-pasta",
"2003-02-16-sauce",
"2003-04-07-cavatelli",
"2004-04-26-sauce",
"2006-02-14-sauce"]
}
]}
As desired, I have all of the recipes that I wanted in the "value" attribute of the results.

Rather than diving into code, I finish off my work tonight by prototyping how I might pull back those recipes for ultimately displaying them in the recipes. I will need the title of the recipes in addition to the IDs. For that, I create another, simple recipe view:
function(doc) {
if (doc['type'] == 'Recipe') {
emit(doc['_id'], doc['title']);
}
}
Given a document ID, such as "2002-08-12-sauce", this "title" view will return the recipe's title. In the case of our tomato sauce recipes, that will amount to 7 separate calls to the CouchDB server. I would just as soon avoid that if I can. And I can using a somewhat obscure feature of CouchDB views: POSTing multiple keys to get an array result. In this case, I take the results of the "alternatives" view for the tomato sauce recipes and POST it to the title view to find:
cstrom@jaynestown:~/repos/eee-code$ curl -X POST \
http://localhost:5984/eee/_design/recipes/_view/titles \
-d '{"keys": ["2002-08-12-sauce","2003-01-17-sausage_pasta","2003-02-13-pasta","2003-02-16-sauce","2003-04-07-cavatelli","2004-04-26-sauce","2006-02-14-sauce"]}'
{"total_rows":578,"offset":175,"rows":[
{"id":"2002-08-12-sauce","key":"2002-08-12-sauce","value":"Tomato Sauce"},
{"id":"2003-01-17-sausage_pasta","key":"2003-01-17-sausage_pasta","value":"Farfalle with Spicy Sausage Sauce"},
{"id":"2003-02-13-pasta","key":"2003-02-13-pasta","value":"Penne with Vodka Sauce"},
{"id":"2003-02-16-sauce","key":"2003-02-16-sauce","value":"Sausage Cream Sauce"},
{"id":"2003-04-07-cavatelli","key":"2003-04-07-cavatelli","value":"Cavatelli with Mushroom Tomato Sauce"},
{"id":"2004-04-26-sauce","key":"2004-04-26-sauce","value":"Quick Tomato Sauce"},
{"id":"2006-02-14-sauce","key":"2006-02-14-sauce","value":"Thick Tomato Sauce"}
]}
That is a good stopping point for today. With some successful prototyping completed, I will pick up with implementation tomorrow.

No comments:

Post a Comment