One of the simplest ways to learn about views is to create a basic map function which extracts data from entries. Imagine we have our own blog application and we want to provide a list of blog posts by title. First imagine what the JSON documents would look like for our blog posts:
{ "title":"Move Today", "body":"We just moved into a new big apartment in Mountain View just off of....", "date":"2012/07/30 18:12:10" } { "title":"Bought New Fridge", "body":"Our freezer broke down so ordered this new one on Amazon....", "date":"2012/09/17 21:13:39" } { "title":"Paint Ball", "body":"Had so much fun today when my company took the whole team out for...", "date":"2012/9/25 15:52:20" }
Then we create our map function which will extract our blog post titles:
function(doc) { if(doc.title) { emit(doc.title, null); } }
This function will look at a JSON document and if the document has
a title attribute, it will include that title
in the result set as a key. The null indicates
no value should be provided in the result set. In reality if you
look at all the details, a standard view function syntax is a bit
more complex in Couchbase 2.0.
Here is how the map function appears when you provide full handling of all JSON document information:
function (doc, meta) { if (meta.type == "json" && doc.title && doc.date) { // Check if doc is JSON emit(doc.title, doc.date); } else { // do something with binary value } }
As a best practice we want make sure that the fields we want to
emit in our index actually exist before we emit it to the index.
Therefore we have our map function within a conditional:
if (doc.title && doc.date). For
instance, if we wanted to perform a views function that tried to
emit doc.name.length we would get a "undefined
reference" exception if the field does not exist and the view
function would fail. By checking for the field we avoid these
potential types of errors.
If you have ever looked at a view in Couchbase Admin Console, this
map function will be more familiar. In Couchbase 2.0 we separate
metadata about an entry such as expiration and the entry itself
into two parts in a JSON document. So in our function we have the
parameter meta for all document meta-data and
doc as the parameter for document values, such
as the title and blog text. Our function first looks at the
metadata to determine if it is a JSON document by doing a
if..else. If the document is JSON, the map
function extracts the blog title and the date/time for the blog
entry. For more information about how meta-data is stored and
handled in JSON documents, see
Couchbase Server 2.0 Manual, Metadata.
If the document is binary data, you would need to provide some code to handle it, but typically if you are going to query an index data, you would do so on JSON documents. For more information about using views with binary data, see Couchbase Server 2.0 Manual, Views on Non-JSON data .
The emit() function takes two arguments: the
first one is key, and the second one is
value. The emit() creates an
entry, or row, in our result set. You are able to call the
emit function multiple times in a map function;
this will create multiple entries in the result set from a single
document. We will discuss that more in depth later.
Once you have your view functions, you store them to Couchbase Server and then query the view to get the result set. When you query your view, Couchbase Server takes the code in your view and runs it on every document persisted on disk. You store your map function as a string in a design document as follows:
{ "_id": "_design/blog", "language": "javascript", "views": { "titles": { "map": "function(doc, meta){ if (meta.type == "json" && doc.date && doc.title) { // Check if doc is JSON emit(doc.date, doc.title); } else { // do something with binary value } } } }
All design documents are prefixed with the id
_design/ and then your name for the design
document. We store all view functions in the
views attribute and name this particular view
titles. Using a Couchbase SDK, you can read the
design document in as a file from the file system and store the
design document to the server. In this case we name our design
document file blog.json:
client = Couchbase.connect("http://localhost:8091/pools/default/buckets/bucketName") client.save_design_doc(File.open('blog.json'))
This code will create a Couchbase client instance with a
connection to the bucket, bucketName. We then
read the design document into memory and write it to Couchbase
Server. At this point we can query the view and retrieve our map
function results:
posts = client.design_docs['blog'] posts.views #=> ["titles"] posts.titles
Couchbase Server will take each document on disk, determine if the document is JSON and then put the blog title and date into a list. Each row in that list includes a key and value:
Key Value "2012/07/30 18:12:10" "Move Today" "2012/09/17 21:13:39" "Bought New Fridge" "2012/09/25 15:52:20" "Paint Ball"
You may wonder how effective it is to run query your view if Couchbase Server will run it on every persisted document in the database. But Couchbase Server is designed to avoid duplicate work. It will run the function on all documents once, when you first query the view. For subsequent queries on the view Couchbase Server will recompute the keys and values only for documents that have changed.
When you query this view, Couchbase Server will send the list of all documents as JSON. It will contain the key, value and the document id, plus some additional metadata. For more information about JSON document metadata in Couchbase, see Couchbase Server Manual 2.0, Document Metadata