view tutorial.html @ 89:c96ebbbe03d6

Fixed title
author Atul Varma <varmaa@toolness.com>
date Mon, 20 Apr 2009 18:10:22 -0700
parents ebf8936fc696
children 093e51c6aa3c
line wrap: on
line source

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
  <link rel="stylesheet" type="text/css" media="all"
        href="css/tutorial.css" />
  <title>BrowserCouch Tutorial</title>
</head>
<body>
<div id="content" class="documentation">
<h1>BrowserCouch Tutorial</h1>

<p>This is a brief introduction to using the BrowserCouch API and the
MapReduce mechanism. If you haven't already read it, you may want to
check out the <a class="intra-wiki" href="index.html">introduction</a>
to learn more about why this style of querying is being explored as an
alternative to SQL for client-side Web Storage.</p>

<p>It should also be noted that BrowserCouch is by no means "mature"
software. It currently lacks a lot of CouchDB's features that it ought
to have, and its API is not stable at all.</p>

<p>Finally, a note about the code examples in this tutorial: they're
actually being executed in your browser, and their output is sometimes
being displayed in this tutorial too. While this helps ensure that the
software is working as intended and also allows for some interactive
learning opportunities, right now it also means that some parts of the
code examples may look a bit unusual. Furthermore, if you see any
conspicuously blank areas in this tutorial, it could be because the
tutorial code crashed&mdash;our apologies if this occurs.</p>

<p>With that out of the way, let's get started.</p>

<h1>Getting Started</h1>

<p>Suppose we want to add offline support for a blog.  To get a
database called <tt>blog-posts</tt> in BrowserCouch, you can use the
following function:</p>

<div class="example-code">
BrowserCouch.get('blog-posts', onRetrieveCb, new FakeStorage());
</div>

<p>It's clear that the first parameter is the name of the database we
want; the second parameter is the callback that will be passed the
database once it's fetched.</p>

<p>The third parameter specifies the engine that will be used to
persistently store our database across browsing sessions. In this case
we're using <tt>FakeStorage</tt>, which just stores everything
non-persistently in memory for the sake of example. We could just as
easily leave out the third parameter to have BrowserCouch figure out
the best storage backend based on our browser's capabilities.</p>

<p>If the database doesn't already exist, an empty one will be created
for us. Putting blog posts into the database can be done via
an <tt>onRetrieveCb()</tt> function like this:</p>

<div class="example-code">
function onRetrieveCb(db) {
  blogDb = db;
  blogDb.put(
    [{id: 0, author: 'Myk', title: 'Burritos', content: 'Burritos are yum.'},
     {id: 1, author: 'Thunder', title: 'Bacon', content: 'I like bacon.'},
     {id: 2, author: 'Thunder', title: 'Beer', content: 'Beer is good too.'}],
    onPutCb
  );
};
</div>

<p>Every item we put into our database needs to have an <tt>id</tt>
attribute, but aside from that, the item can contain any
JSON-encodable data.</p>

<p>Now that we've put some data into our database, we can play around
with generating views on the data using the <a
href="http://en.wikipedia.org/wiki/MapReduce">MapReduce</a> mechanism.
For instance, here's an ad-hoc view using only the map phase that
organizes all the post titles by author:</p>

<div class="example-code">
function onPutCb() {
  blogDb.view({
    map: function(doc, emit) {
      emit(doc.author, doc.title);
    },
    finished: function(result) {
      displayInElement(result, 'author-keyed-view');
      tryAnotherView();
    }
  });
}
</div>

<p>The output placed in the <tt>author-keyed-view</tt> element is:</p>

<div class="example-output" id="author-keyed-view"></div>

<p>As you can see, BrowserCouch essentially iterated over all of the
blog posts, passing each one to <tt>map()</tt>, along with an
arbitrary function called <tt>emit()</tt>.  The <tt>map()</tt>
function then emitted key-value pairs which show up in the view. It's
worth noting that <tt>map()</tt> can call <tt>emit()</tt> as much as
it wants to; each call will add a new row to the view.</p>

<p>We could also try creating another view that adds a
<tt>reduce()</tt> function to group together the blog post titles with
the authors:</p>

<div class="example-code">
function tryAnotherView() {
  blogDb.view({
    map: function(doc, emit) {
      emit(doc.author, doc.title);
    },
    reduce: function(keys, values) {
      return values;
    },
    finished: function(result) {
      displayInElement(result, 'author-titles-view');
      findRows(result);
    }
  });
}
</div>

<p>The output is as follows:</p>

<div class="example-output" id="author-titles-view"></div>

<p>The <tt>reduce()</tt> mechanism is a bit harder to
understand. Essentially, BrowserCouch takes all the rows generated by
<tt>map()</tt> and generates a new list of key-value rows, where the
value of each row is the list of all values that match the row's key.
This explains what the <tt>values</tt> argument passed to
<tt>reduce()</tt> is.</p>

<p>The <tt>keys</tt> argument is a list of 2-tuples, the first of
which is the key, and the second of which is the document id that
emitted the key during the map phase.</p>

<p>The <tt>reduce()</tt> function is called for each unique key, and
its return value is the value for its key in the final view.</p>

<p>Once you've got a view, you can use the view's <tt>findRow()</tt>
method to find the first row whose key matchest (or is closest to) the
one you provide.  For example:</p>

<div class="example-code">
function findRows(result) {
    var rowIndex = result.findRow('Thunder');
    displayInElement(result.rows[rowIndex], 'author-find-row-view');
    tryMyView();
}
</div>

<p>The output for this one is:</p>

<div class="example-output" id="author-find-row-view"></div>

<h1>Now You Try!</h1>

<p>If your eyes are crossed right now, no worries&mdash;most people
take a long time to understand exactly what MapReduce is doing. That
said, the easiest way to understand how MapReduce works is just to
play around with creating your own view.</p>

<p>So, to get a better feel for how MapReduce works, you can use the
text field below to try making your own view. Just press the tab key
when you're done making changes to recompute the view.</p>

<textarea class="example-code try-code">
function tryMyView() {
  blogDb.view({
    map: function(doc, emit) {
      emit(doc.author, doc.title);
    },
    reduce: function(keys, values) {
      return values;
    },
    finished: function(result) {
      displayInElement(result, 'try-my-view');
    }
  });
}
</textarea>

<p>Here's the output to the above view:</p>

<div class="example-output" id="try-my-view"></div>

<h1>Where To Go From Here</h1>

<p>There's features in the API that aren't covered here, so check out
the check out the <a class="intra-wiki"
href="index.html#js/tests.js">test suite's annotated source code</a>
for more examples.</p>

<script src="js/ext/jquery.js"></script>
<script src="js/browser-couch.js"></script>
<script src="js/tutorial.js"></script>
</body>
</html>