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&mdash;our apologies if this occurs.</p> 31 in this tutorial, it could be because the tutorial code
32 crashed&mdash;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&mdash;most people 170 <p>If your eyes are crossed right now, no worries&mdash;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>