Mercurial > browser-couch
comparison tutorial.html @ 95:86158b61b732 blog-post
Reworked the tutorial so that we don't have to have each snippet be a callback from the previous snippet, which makes the tutorial a lot harder to follow.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Tue, 21 Apr 2009 12:13:18 -0700 |
parents | af36b00306d0 |
children |
comparison
equal
deleted
inserted
replaced
94:97820cdda26f | 95:86158b61b732 |
---|---|
22 to have, and its API is not stable at all.</p> | 22 to have, and its API is not stable at all.</p> |
23 | 23 |
24 <p>Finally, a note about the code examples in this tutorial: they're | 24 <p>Finally, a note about the code examples in this tutorial: they're |
25 actually being executed in your browser, and their output is sometimes | 25 actually being executed in your browser, and their output is sometimes |
26 being displayed in this tutorial too. While this helps ensure that the | 26 being displayed in this tutorial too. While this helps ensure that the |
27 software is working as intended and also allows for some interactive | 27 software is working as intended and also allows for some <a |
28 learning opportunities, right now it also means that some parts of the | 28 class="intra-wiki" href="#try">interactive learning opportunities</a>, |
29 code examples may look a bit unusual. Furthermore, if you see any | 29 right now it also means that some parts of the code examples may look |
30 conspicuously blank areas in this tutorial, it could be because the | 30 a bit unusual. Furthermore, if you see any conspicuously blank areas |
31 tutorial code crashed—our apologies if this occurs.</p> | 31 in this tutorial, it could be because the tutorial code |
32 crashed—our apologies if this occurs.</p> | |
32 | 33 |
33 <p>With that out of the way, let's get started.</p> | 34 <p>With that out of the way, let's get started.</p> |
34 | 35 |
35 <h1>Getting Started</h1> | 36 <h1>Getting Started</h1> |
36 | 37 |
37 <p>Suppose we want to add offline support for a blog. To get a | 38 <p>Suppose we want to add offline support for a blog. To get a |
38 database called <tt>blog-posts</tt> in BrowserCouch, you can use the | 39 database called <tt>blog-posts</tt> in BrowserCouch, you can use the |
39 following function:</p> | 40 following function:</p> |
40 | 41 |
41 <div class="example-code"> | 42 <div class="example-code"> |
42 BrowserCouch.get('blog-posts', onRetrieveCb, new FakeStorage()); | 43 BrowserCouch.get('blog-posts', |
44 function onRetrieveCb(db) { | |
45 blogDb = db; /* Save the DB for later. */ DONE(); | |
46 }, | |
47 new FakeStorage()); | |
43 </div> | 48 </div> |
44 | 49 |
45 <p>It's clear that the first parameter is the name of the database we | 50 <p>It's clear that the first parameter is the name of the database we |
46 want; the second parameter is the callback that will be passed the | 51 want; the second parameter is the callback that will be passed the |
47 database once it's fetched.</p> | 52 database once it's fetched.</p> |
52 non-persistently in memory for the sake of example. We could just as | 57 non-persistently in memory for the sake of example. We could just as |
53 easily leave out the third parameter to have BrowserCouch figure out | 58 easily leave out the third parameter to have BrowserCouch figure out |
54 the best storage backend based on our browser's capabilities.</p> | 59 the best storage backend based on our browser's capabilities.</p> |
55 | 60 |
56 <p>If the database doesn't already exist, an empty one will be created | 61 <p>If the database doesn't already exist, an empty one will be created |
57 for us. Putting blog posts into the database can be done via | 62 for us. Putting blog posts into the database is done through the |
58 an <tt>onRetrieveCb()</tt> function like this:</p> | 63 <tt>put()</tt> method like so:</p> |
59 | 64 |
60 <div class="example-code"> | 65 <div class="example-code"> |
61 function onRetrieveCb(db) { | 66 blogDb.put( |
62 blogDb = db; | 67 [{id: 0, author: 'Myk', title: 'Burritos', content: 'Burritos are yum.'}, |
63 blogDb.put( | 68 {id: 1, author: 'Thunder', title: 'Bacon', content: 'I like bacon.'}, |
64 [{id: 0, author: 'Myk', title: 'Burritos', content: 'Burritos are yum.'}, | 69 {id: 2, author: 'Thunder', title: 'Beer', content: 'Beer is good too.'}], |
65 {id: 1, author: 'Thunder', title: 'Bacon', content: 'I like bacon.'}, | 70 function onDone() { /* Do stuff... */ DONE();} |
66 {id: 2, author: 'Thunder', title: 'Beer', content: 'Beer is good too.'}], | 71 ); |
67 onPutCb | |
68 ); | |
69 }; | |
70 </div> | 72 </div> |
71 | 73 |
72 <p>Every item we put into our database needs to have an <tt>id</tt> | 74 <p>Every item we put into our database needs to have an <tt>id</tt> |
73 attribute, but aside from that, the item can contain any | 75 attribute, but aside from that, the item can contain any |
74 JSON-encodable data.</p> | 76 JSON-encodable data.</p> |
77 | |
78 <h1>Views</h1> | |
75 | 79 |
76 <p>Now that we've put some data into our database, we can play around | 80 <p>Now that we've put some data into our database, we can play around |
77 with generating views on the data using the <a | 81 with generating views on the data using the <a |
78 href="http://en.wikipedia.org/wiki/MapReduce">MapReduce</a> mechanism. | 82 href="http://en.wikipedia.org/wiki/MapReduce">MapReduce</a> mechanism. |
79 For instance, here's an ad-hoc view using only the map phase that | 83 For instance, here's an ad-hoc view using only the map phase that |
80 organizes all the post titles by author:</p> | 84 organizes all the post titles by author:</p> |
81 | 85 |
82 <div class="example-code"> | 86 <div class="example-code"> |
83 function onPutCb() { | 87 blogDb.view({ |
84 blogDb.view({ | 88 map: function(doc, emit) { |
85 map: function(doc, emit) { | 89 emit(doc.author, doc.title); |
86 emit(doc.author, doc.title); | 90 }, |
87 }, | 91 finished: function(result) { |
88 finished: function(result) { | 92 displayInElement(result, 'author-keyed-view'); DONE(); |
89 displayInElement(result, 'author-keyed-view'); | 93 } |
90 tryAnotherView(); | 94 }); |
91 } | 95 </div> |
92 }); | 96 |
93 } | 97 <p>The <tt>view()</tt> method above has lots of optional arguments, |
94 </div> | 98 which is why we're passing in a single object with keys corresponding |
99 to argument names. The <tt>map</tt> argument is the function to use | |
100 for the map phase, and the <tt>finished</tt> argument is the callback | |
101 to pass the view results into when processing is complete.</p> | |
95 | 102 |
96 <p>The output placed in the <tt>author-keyed-view</tt> element is:</p> | 103 <p>The output placed in the <tt>author-keyed-view</tt> element is:</p> |
97 | 104 |
98 <div class="example-output" id="author-keyed-view"></div> | 105 <div class="example-output" id="author-keyed-view"></div> |
99 | 106 |
102 arbitrary function called <tt>emit()</tt>. The <tt>map()</tt> | 109 arbitrary function called <tt>emit()</tt>. The <tt>map()</tt> |
103 function then emitted key-value pairs which show up in the view. It's | 110 function then emitted key-value pairs which show up in the view. It's |
104 worth noting that <tt>map()</tt> can call <tt>emit()</tt> as much as | 111 worth noting that <tt>map()</tt> can call <tt>emit()</tt> as much as |
105 it wants to; each call will add a new row to the view.</p> | 112 it wants to; each call will add a new row to the view.</p> |
106 | 113 |
107 <p>We could also try creating another view that adds a | 114 <p>At this point you may want to jump to the <a class="intra-wiki" |
108 <tt>reduce()</tt> function to group together the blog post titles with | 115 href="#try">Try It For Yourself</a> section to play around with making |
109 the authors:</p> | 116 your own <tt>map()</tt> functions.</p> |
110 | 117 |
111 <div class="example-code"> | 118 <p>The reduce phase of a view is totally optional and a little |
112 function tryAnotherView() { | 119 confusing. Let's try adding a <tt>reduce()</tt> function to our |
113 blogDb.view({ | 120 earlier view to group together the blog post titles with the |
114 map: function(doc, emit) { | 121 authors:</p> |
115 emit(doc.author, doc.title); | 122 |
116 }, | 123 <div class="example-code"> |
117 reduce: function(keys, values) { | 124 blogDb.view({ |
118 return values; | 125 map: function(doc, emit) { |
119 }, | 126 emit(doc.author, doc.title); |
120 finished: function(result) { | 127 }, |
121 displayInElement(result, 'author-titles-view'); | 128 reduce: function(keys, values) { |
122 findRows(result); | 129 return values; |
123 } | 130 }, |
124 }); | 131 finished: function(result) { |
125 } | 132 authors = result; /* Save the result for later. */ |
133 displayInElement(authors, 'author-titles-view'); DONE(); | |
134 } | |
135 }); | |
126 </div> | 136 </div> |
127 | 137 |
128 <p>The output is as follows:</p> | 138 <p>The output is as follows:</p> |
129 | 139 |
130 <div class="example-output" id="author-titles-view"></div> | 140 <div class="example-output" id="author-titles-view"></div> |
131 | 141 |
132 <p>The <tt>reduce()</tt> mechanism is a bit harder to | 142 <p>Essentially, BrowserCouch takes all the rows generated by |
133 understand. Essentially, BrowserCouch takes all the rows generated by | |
134 <tt>map()</tt> and generates a new list of key-value rows, where the | 143 <tt>map()</tt> and generates a new list of key-value rows, where the |
135 value of each row is the list of all values that match the row's key. | 144 value of each row is the list of all values that match the row's key. |
136 This explains what the <tt>values</tt> argument passed to | 145 This explains what the <tt>values</tt> argument passed to |
137 <tt>reduce()</tt> is.</p> | 146 <tt>reduce()</tt> is.</p> |
138 | 147 |
146 <p>Once you've got a view, you can use the view's <tt>findRow()</tt> | 155 <p>Once you've got a view, you can use the view's <tt>findRow()</tt> |
147 method to find the first row whose key matches (or is closest to) the | 156 method to find the first row whose key matches (or is closest to) the |
148 one you provide. For example:</p> | 157 one you provide. For example:</p> |
149 | 158 |
150 <div class="example-code"> | 159 <div class="example-code"> |
151 function findRows(result) { | 160 var rowIndex = authors.findRow('Thunder'); |
152 var rowIndex = result.findRow('Thunder'); | 161 displayInElement(authors.rows[rowIndex], 'author-find-row-view'); |
153 displayInElement(result.rows[rowIndex], 'author-find-row-view'); | |
154 tryMyView(); | |
155 } | |
156 </div> | 162 </div> |
157 | 163 |
158 <p>The output for this one is:</p> | 164 <p>The output for this one is:</p> |
159 | 165 |
160 <div class="example-output" id="author-find-row-view"></div> | 166 <div class="example-output" id="author-find-row-view"></div> |
161 | 167 |
162 <h1>Now You Try!</h1> | 168 <a name="try"><h1>Try It For Yourself</h1></a> |
163 | 169 |
164 <p>If your eyes are crossed right now, no worries—most people | 170 <p>If your eyes are crossed right now, no worries—most people |
165 take a long time to understand exactly what MapReduce is doing. That | 171 take a long time to understand exactly what MapReduce is doing. That |
166 said, the easiest way to understand how MapReduce works is just to | 172 said, the easiest way to understand how MapReduce works is just to |
167 play around with creating your own view.</p> | 173 play around with creating your own view.</p> |
168 | 174 |
169 <p>You can use the text field below to do just that. Just press the | 175 <p>You can use the text field below to do just that. Just press the |
170 tab key when you're done making changes to recompute the view.</p> | 176 tab key when you're done making changes to recompute the view.</p> |
171 | 177 |
172 <textarea class="example-code try-code"> | 178 <textarea class="example-code try-code"> |
173 function tryMyView() { | 179 blogDb.view({ |
174 blogDb.view({ | 180 map: function(doc, emit) { |
175 map: function(doc, emit) { | 181 emit(doc.author, doc.title); |
176 emit(doc.author, doc.title); | 182 }, |
177 }, | 183 reduce: function(keys, values) { |
178 reduce: function(keys, values) { | 184 return values; |
179 return values; | 185 }, |
180 }, | 186 finished: function(result) { |
181 finished: function(result) { | 187 displayInElement(result, 'try-my-view'); DONE(); |
182 displayInElement(result, 'try-my-view'); | 188 } |
183 } | 189 }); |
184 }); | |
185 } | |
186 </textarea> | 190 </textarea> |
187 | 191 |
188 <p>Here's the output to the above view:</p> | 192 <p>Here's the output to the above view:</p> |
189 | 193 |
190 <div class="example-output" id="try-my-view"></div> | 194 <div class="example-output" id="try-my-view"></div> |