There is a JavaScript embedded in CouchDB: the one that is used in Futon. Lets see how to use it from your CouchApp for executing map/reduce views. This post is a follow on of this one.
Executing a view using jquery.couchdb.js
Background
Invoking a view is done by using:
db.view("design/view", {
success:function (data) {
// Got response from CouchDB
// Process data
// and then return retrieved data
},
error :function (status) {
// Error function...
}
});
Where design is the CouchDB design document storing the view (view).
Using a key in a map function
Lets consider a CouchDB database with cities and states from USA stored in documents with the following structure:
{
"_id": "0254D19B-7360-459E-A9F2-715E36AD1822",
"_rev": "1-d6c3ca78003b4116ecef930d538b2d94",
"type": "cities",
"city": "Pasadena",
"state": "California"
},
{
"_id": "05C7AD05-0CC6-4C6E-A21B-E4C8344C242A",
"_rev": "1-e0a64cd86be76166d3b1592376b8c23c",
"type": "cities",
"city": "Lakewood",
"state": "Colorado"
}
I.e., Documents with field type value cities that allows me to isolate records containing the pairs (city – state) from others stored in the same database; and the the name of the city and the state itself.
Then I have defined the following map function:
function(doc) {
if (doc.type==="cities") emit(doc.state, doc.city );
}
That emits every document of type cities. These emitted records will have as key the name of the state and as value, the name of the city.
If I execute the view using:
db.view("post20/getCities", {
success:function (data) {
console.log(JSON.stringify(data));
},
error :function (status) {
console.log(status);
}
});
Where post20 is the design document and getCities is the name of the view.
I get something like:
{
"total_rows": 285,
"offset": 0,
"rows": [
{"id":"34716A25-2D33-43F7-BDA2-CBC063BC04A4", "key":"Alabama", "value":"Mobile"},
{"id":"4F5D34B0-8F24-4C84-98D3-6F104EB19C42", "key":"Alabama", "value":"Montgomery"},
{"id":"B3BB27B0-9D6E-4776-A685-67378E9198A3", "key":"Alabama", "value":"Birmingham"},
{"id":"91B00C11-6648-4E15-83F2-957561ACE157", "key":"Alaska", "value":"Anchorage"},
{"id":"212EA2DA-40C3-4443-AFB7-91366891D3D5", "key":"Arizona", "value":"Tucson"},
{"id":"7132A69F-B1CE-4AB6-ABF8-22EC6745C35D", "key":"Arizona", "value":"Phoenix"},
{"id":"9938B89F-E119-47AA-9186-F15BDEF557E1", "key":"Arizona", "value":"Surprise"},
{"id":"A2E9C9F5-033F-454D-8B54-3A303F2E13C2", "key":"Arizona", "value":"Peoria"},
{"id":"BFD01743-2297-47B1-B997-4B63B9BF4213", "key":"Arizona", "value":"Mesa"},
...
}
But, I want to choose only the cities from California then I have to set California as key (and that’s why I defined the map function that emits the state as key. To send the key to CouchDB I have to say:
db.view("post20/getCities", {
success:function (data) {
console.log(JSON.stringify(data));
},
error :function (status) {
console.log(status);
},
key :"California"
});
and I get as result:
{
"total_rows":285,
"offset" :16,
"rows" :[
{"id":"0254D19B-7360-459E-A9F2-715E36AD1822", "key":"California", "value":"Pasadena"},
{"id":"07A64E55-2AB1-4AA2-81E8-3D1D54508A26", "key":"California", "value":"Vallejo"},
{"id":"07CB593A-8F8A-4100-B650-BB949775EBA4", "key":"California", "value":"Ontario"},
{"id":"0884A3CD-2D54-4B4C-9492-4BCF168F04DA", "key":"California", "value":"Salinas"},
{"id":"0C835A0A-6109-4986-904B-5C6102D86EB4", "key":"California", "value":"San Buenaventura (Ventura)"},
{"id":"10687A90-F40B-4154-BF46-A1D976C8E800", "key":"California", "value":"Fresno"},
...
]
}
Neat!
What about if I want to retrieve the list of cities of Alaska and Wisconsin?
db.view("post20/getCities", {
success:function (data) {
console.log(JSON.stringify(data));
},
error :function (status) {
console.log(status);
},
keys :[ "Alaska", "Wisconsin" ]
});
and I get:
{"total_rows":285, "offset":4, "rows":[
{"id":"91B00C11-6648-4E15-83F2-957561ACE157", "key":"Alaska", "value":"Anchorage"},
{"id":"0A2056EC-4C2E-4285-8CCE-EA0F4C71D353", "key":"Wisconsin", "value":"Milwaukee"},
{"id":"322DC6B3-3B5E-4310-B674-6957A40A4D03", "key":"Wisconsin", "value":"Green Bay"},
{"id":"E1371AA5-18DB-48A4-AD90-B212030E6C6F", "key":"Wisconsin", "value":"Madison"}
]}
NOTE: Realize that I have had to change key by keys (plural) and then define the list of values to choose from.
Using reduce
Lets define a reduce function for getting the states of our database.
Getting unique values in CouchDB
Using this same database, if I want to retrieve the states selecting the key of the already defined map function or even if I define a map function as:
function(doc) {
if (doc.type==="cities") emit(null, doc.state );
}
I get the states as many times, as cities for that state I have in the database (I get states duplicated).
{
"total_rows":285,
"offset" :0,
"rows" :[
{"id":"0254D19B-7360-459E-A9F2-715E36AD1822", "key":null, "value":"California"},
{"id":"02C0F725-A811-492F-BCC0-8921E04899F4", "key":null, "value":"Indiana"},
{"id":"04A29C30-CCDC-4D73-B9D4-D8ABBBA9AB46", "key":null, "value":"Texas"},
{"id":"05C7AD05-0CC6-4C6E-A21B-E4C8344C242A", "key":null, "value":"Colorado"},
{"id":"07A64E55-2AB1-4AA2-81E8-3D1D54508A26", "key":null, "value":"California"},
{"id":"07CB593A-8F8A-4100-B650-BB949775EBA4", "key":null, "value":"California"},
{"id":"07F27430-1A5A-4F12-9116-C720AE84E246", "key":null, "value":"Utah"},
{"id":"0884A3CD-2D54-4B4C-9492-4BCF168F04DA", "key":null, "value":"California"},
{"id":"090E0E6C-A4EC-4E84-B071-5C771ADA3DB2", "key":null, "value":"Minnesota"},
{"id":"094E4363-69EA-443A-964D-29F3B2F04546", "key":null, "value":"Texas"},
{"id":"0A2056EC-4C2E-4285-8CCE-EA0F4C71D353", "key":null, "value":"Wisconsin"},
{"id":"0C835A0A-6109-4986-904B-5C6102D86EB4", "key":null, "value":"California"},
{"id":"0CFD3158-AD42-448B-83A8-BB13C5FF87EF", "key":null, "value":"Washington"},
{"id":"0D3EE837-0BC1-47CE-A8FA-3FA54400549C", "key":null, "value":"Ohio"},
{"id":"10687A90-F40B-4154-BF46-A1D976C8E800", "key":null, "value":"California"}
...
]
}
The way of getting only unique values in CouchDB is defining a reduce function as follow:
function(keys, values) {
return true;
}
and then ask CouchDB for reducing and grouping the result of map function.
Using map/reduce in jquery.couchdb.js
Choosing to reduce the result is not much different, basically I have to define the reduce function for our view:
function(keys, values) {
return true;
}
And invoking view in jquery.couchdb is:
db.view("post20/getCities", {
success:function (data) {
console.log(JSON.stringify(data));
},
error :function (status) {
console.log(status);
},
reduce: true,
group: true
});
And I get:
{"rows":[
{"key":"Alabama", "value":true},
{"key":"Alaska", "value":true},
{"key":"Arizona", "value":true},
{"key":"Arkansas", "value":true},
{"key":"California", "value":true},
...
]}
NOTE: As an alternative to this reduce function, I might have decided to provide a little more functionality by choosing to return as value the number of cities found for that state. If so, I can simply use a built-in function called _count and my reduce would be:
_count
Yes! just _count, as I said its a built-in function and then I don’t need to provide the body of the function, the name is enough.
I would invoke the view in the same way (with reduce and group set to true) and I get:
{
"rows":[
{"key":"Alabama", "value":4},
{"key":"Alaska", "value":1},
{"key":"Arizona", "value":10},
{"key":"Arkansas", "value":1},
{"key":"California", "value":69},
...
]
}
Summarizing
Adding extra attributes to a view is adding extra pairs of key – value to the options (second argument) of view method. Here I’ve covered key, keys, reduce and group but you can also use others as startkey and endkey:
db.view("post20/getCities", {
success:function (data) {
console.log(JSON.stringify(data));
},
error :function (status) {
console.log(status);
},
group :true,
reduce :true,
startkey: "Arizona",
endkey: "Colorado"
});
That retrieves:
{"rows":[
{"key":"Arizona", "value":true},
{"key":"Arkansas", "value":true},
{"key":"California", "value":true},
{"key":"Colorado", "value":true}
]}