Andy Wenk
May 10, 2012 - JUG-OSTFALEN

 

 

 

part I:    Introduction to CouchDB
part II:   A CouchApp with Kanso

/ Andy Wenk

/ Senior Developer at SinnerSchrader Hamburg

/ Having fun with Ruby, Rails, JavaScript, HTML5, CSSx, PHP

/ ... and CouchDB!

/ had fun with PostgreSQL also

 

/ get in touch:

andy@nms.de
andreas.wenk@sinnerschrader.com
G+
@awenkhh
couchdb-buch.de

What you will hear ...

CouchDB is built of the Web
commonly used phrase
Relational databases have been very successful in the past, and are likely to remain so, but in a certain sub-set of usage scenarios, non-relational systems are starting to take their place.
Michael Lenahan

NoSQL database overview (incomplete, steadily growing)

see also: http://kkovacs.eu/cassandra-vs-mongodb-vs-couchdb-vs-redis/

Cassandra (http://cassandra.apache.org/)

Usage:

mongoDB (http://www.mongodb.org/)

Usage:

neo4J (http://neo4j.org/)

Usage:

riak (http://www.basho.com/Riak.html)

Usage:

Reading: http://riakhandbook.com/ by Mathias Meyer (@roidrage)

redis (http://redis.io/)

Usage:

try it: http://try.redis-db.com/

Overview

Language Access designed for
CouchDB Erlang HTTP / REST consistency, ease of use
Cassandra Java driver, thrift Best of BigTable and Dynamo
MongoDB C++ driver good parts of SQL
Neo4J Java REST / embedded relations, search
riak Erlang REST / driver Fault tolerance, tunable
redis C driver / TCP speed

What is CouchDB? - technically?

What is CouchDB? MVCC and ACID

MVCC - Multi Version Concurrency Control


ACID

What is CouchDB? MapReduce

... later

What is CouchDB? CAP

CAP Theorem by Dr. Eric Brewer - approved by Seth Gilbert and Nancy Lynch

In CouchDB when used as distributed system: AP


Towards Robust Distributed Systems
Brewer’s Conjecture and the Feasibility of Consistent, Available, Partition-tolerant Web Services
Cloudant's BigCouch
CouchDB Lounge

What is CouchDB? CAP


Installing CouchDB

Mac OS X - homebrew

brew update
brew upgrade
brew install couchdb
          
Trouble (e.g. because 1.1.1 is installed)?
brew unlink couchdb
brew install couchdb
couchdb -V
  couchdb - Apache CouchDB 1.2.0
          

Installing CouchDB

Mac OS X - MacPorts (rethink!)

sudo port install couchdb
sudo port upgrade couchdb
          

Linux

Instructions for Ubuntu
Instructions for all Linux OS

Windows

Instructions

Querying CouchDB with http - db level

Hello CouchDB:

curl -X GET http://localhost:5984 
{"couchdb":"Welcome","version":"1.2.0"}
          

All DB's

curl -X GET http://localhost:5984/_all_dbs
["_replicator","_users","kassenbuch","multi-ids"]
          

Create a DB

curl -X PUT http://localhost:5984/jug
{"ok":true}
curl -X GET http://localhost:5984/_all_dbs
["_replicator","_users","jug","kassenbuch","multi-ids"]
          

Querying CouchDB with http - document level

Creating a document with POST

curl -X POST http://localhost:5984/jug/ -H "Content-Type: application/json" -d '{}'
{"ok":true,
 "id":"03cab906fa68e852be833ebb17001cd4",
 "rev":"1-967a00dff5e02add41819138abb3284d"}

id

uuid #provided by CouchDB

rev

(revision number)-(md5 hash of the document content)

Annotation

# using POST to create a document requires the Content-Type header
# the document _id will be created by CouchDB

Querying CouchDB with http - document level

Creating a document with PUT with usage of a uuid ...

curl -X GET http://localhost:5984/_uuids
{"uuids":["03cab906fa68e852be833ebb17000ff8"]}
curl -X PUT http://localhost:5984/jug/03cab906fa68e852be833ebb17000ff8 -d '{}'
{"ok":true,
 "id":"03cab906fa68e852be833ebb17000ff8",
 "rev":"1-967a00dff5e02add41819138abb3284d"}

... or with a own id

curl -X PUT http://localhost:5984/jug/first_doc -d '{}'
{"ok":true,"id":"first_doc","rev":"1-967a00dff5e02add41819138abb3284d"}
          

Annotation

# using PUT to create a document requires to provide the document _id

Querying CouchDB with http - document level

Updating a document with PUT

curl -X PUT http://localhost:5984/jug/first_doc 
-d '{"_rev":"1-967a00dff5e02add41819138abb3284d",
     "some-content":"Hey how are you dude?"}'
{"ok":true,"id":"first_doc","rev":"2-65ef50076c98c6817fb3ca97c87d8b66"}
          

possible to use the if-match header

-H "if-match:1-967a00dff5e02add41819138abb3284d"
          

Annotation

# when updating a document, the most actual _rev of the document has to be provided

Querying CouchDB with http - document level

Difference between POST and PUT

 

Annotation

read POST and the following PUT paragraph from RFC2616

Querying CouchDB with http - document level

Deleting a document with GET

curl -X DELETE http://localhost:5984/jug/first_doc?rev=3-73846195cbf86c1a3b94b6c04b6bebc0
{"ok":true,"id":"first_doc","rev":"4-9853b2dfc30eb372590c60a339124cfd"}
          

possible to use the if-match header

-H "if-match:3-73846195cbf86c1a3b94b6c04b6bebc0"
          

document is not deleted physically but marked with --deleted: true

curl -X GET http://localhost:5984/jug/first_doc
{"error":"not_found","reason":"deleted"}
          

Querying CouchDB with http - document level

Add attachment to document

curl -X PUT http://localhost:5984/jug/second_doc -d '{}'
{"ok":true,"id":"second_doc","rev":"1-967a00dff5e02add41819138abb3284d"}

curl -X PUT http://localhost:5984/jug/second_doc/mvcc.png?rev=1-967a00dff5e02add41819138abb3284d 
 --data-binary @mvcc.png 
 -H "Content-Type: image/png"
{"ok":true,"id":"second_doc","rev":"2-f488b45f8c02c4da096d23739051d428"}
          

Querying CouchDB with http - document level

Receive attachment from document

curl -i -X GET http://localhost:5984/jug/second_doc/
HTTP/1.1 200 OK
Server: CouchDB/1.2.0 (Erlang OTP/R15B01)
ETag: "2-f488b45f8c02c4da096d23739051d428"
Date: Sun, 06 May 2012 21:03:26 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 200
Cache-Control: must-revalidate

{"_id":"second_doc",
 "_rev":"2-f488b45f8c02c4da096d23739051d428",
"_attachments":
 {"mvcc.png":
  {"content_type":"image/png",
   "revpos":5,
   "digest":"md5-RJsN7gjQubg+iqLe96+SxQ==",
   "length":12753,
   "stub":true
  }
 }
}
          

Querying CouchDB with http - document level

Copy the documents content into another one

curl -i -X PUT http://localhost:5984/jug/third_doc -d '{"first_name": "Andy"}'
{"ok":true,"id":"third_doc","rev":"1-16e80e6f62d44c4569d13e2e7a05e373"}

curl -X PUT http://localhost:5984/jug/fourth_doc -d '{"last_name": "Wenk"}'
{"ok":true,"id":"fourth_doc","rev":"1-18236196ad905238b2b24d33a12eb043"}

curl -X COPY http://localhost:5984/jug/third_doc 
-H 'destination:fourth_doc?rev=1-18236196ad905238b2b24d33a12eb043'
{"ok":true,"id":"fourth_doc","rev":"2-21431c6bda57d30383987ae2510c309e"}

curl -X COPY http://localhost:5984/jug/third_doc -H 'destination:fifth_doc'
{"ok":true,"id":"fifth_doc","rev":"1-993591377f6cb544ddea933a6ed28e38"}
          

Copying third_doc to fourth_doc is overwriting not merging!

Copying third_doc to fifth_doc will create the fith_doc newly

Querying CouchDB with http - document level

Get all documents

curl -X GET http://localhost:5984/jug/_all_docs
{"total_rows":1,"offset":0,"rows":[
{"id":"03cab906fa68e852be833ebb17000ff8","key":"03cab906fa68e852be833ebb17000ff8","value":{"rev":"1-967a00dff5e02add41819138abb3284d"}}
]}
          

Querying CouchDB with http - document level

Check also:

Retrieving data with Map/Reduce

Invented by Google


Wiki: MapReduce
Google’s MapReduce Programming Model — Revisited

Retrieving data with Map/Reduce

some documents with structure

{
   "_id": "f165fc62ed5d14ca84128de1d6034a36",
   "_rev": "1-357fda3050771ca747b95c0108a21fe6",
   "type": "document_store",
   "name": "CouchDB"
}
          

simplest possible view

{
   "_id": "_design/simple",
   "_rev": "2-0404067f53ecc72bc660b8bed7576604",
   "views": {
       "all_database_types": {
           "map": "function (doc) { emit(doc.type, doc.name); }"
       }
   }
}
          

Retrieving data with Map/Reduce

firing the view

curl -X GET 'http://localhost:5984/map-reduce/_design/simple/ \
  _view/all_database_types'
{"total_rows":12,"offset":0,"rows":[
{"id":"f165fc62ed5d14ca84128de1d6036c8c","key":"column_store","value":"BigTable"},
{"id":"f165fc62ed5d14ca84128de1d60375a4","key":"column_store","value":"Hadoop"},
{"id":"f165fc62ed5d14ca84128de1d6037f0e","key":"column_store","value":"Cassandra"},
{"id":"f165fc62ed5d14ca84128de1d6038002","key":"column_store","value":"HBase"},
{"id":"f165fc62ed5d14ca84128de1d6034a36","key":"document_store","value":"CouchDB"},
{"id":"f165fc62ed5d14ca84128de1d6035544","key":"document_store","value":"MongoDB"},
{"id":"f165fc62ed5d14ca84128de1d60358c0","key":"document_store","value":"Riak"},
{"id":"f165fc62ed5d14ca84128de1d60367ac","key":"document_store","value":"Amazon SimpleDB"},
{"id":"f165fc62ed5d14ca84128de1d60387fb","key":"key_value","value":"couchbase"},
{"id":"f165fc62ed5d14ca84128de1d6038f3f","key":"key_value","value":"redis"},
{"id":"f165fc62ed5d14ca84128de1d6039f11","key":"key_value","value":"berkley DB"},
{"id":"f165fc62ed5d14ca84128de1d603a8b6","key":"key_value","value":"Raptor DB"}
]}
          

attention: start using single quotes for the URL - otherwise cURL will fuck up!

Retrieving data with Map/Reduce

firing the view and receive only data for type=column_store

curl -X GET 'http://localhost:5984/map-reduce/_design/simple/ \
  _view/all_database_types?key="document_store"'
{"total_rows":12,"offset":4,"rows":[
{"id":"f165fc62ed5d14ca84128de1d6034a36","key":"document_store","value":"CouchDB"},
{"id":"f165fc62ed5d14ca84128de1d6035544","key":"document_store","value":"MongoDB"},
{"id":"f165fc62ed5d14ca84128de1d60358c0","key":"document_store","value":"Riak"},
{"id":"f165fc62ed5d14ca84128de1d60367ac","key":"document_store","value":"Amazon SimpleDB"}
]}
          

See also: http://wiki.apache.org/couchdb/HTTP_view_API

Retrieving data with Map/Reduce

extending the view with a reduce function

{
   "_id": "_design/simple",
   "_rev": "2-0404067f53ecc72bc660b8bed7576604",
   "views": {
       "all_database_types": {
           "map": "function (doc) { emit(doc.type, doc.name); }"
           "reduce": "_count"
       }
   }
}
          
curl -X GET 'http://localhost:5984/map-reduce/_design/simple/ \
  _view/all_database_types?group=true'
{"rows":[
{"key":"column_store","value":4},
{"key":"document_store","value":4},
{"key":"key_value","value":4}
]}
          

If you want to fire the view without reduce, use the parameter reduce=false

Retrieving data with Map/Reduce

See also:

_show functions

 

_show functions

basic _design document structure

{
  "_id": "_design/simple",
  "_rev": "13-49b3f4710c9b9ecb4bdd9ae8a8ed6c8b",
  "views": {
      "name": {
          "map": "...",
          "reduce": "..."
      }
  },
  "shows": {
      "name": "..."
  },
  "lists": {
      "name": "..."
  },
  "language": "javascript"
}
          

_show functions - example with result

"shows": {
  "html": "function(doc, req){log(doc); log(req); if(doc) {return \
    '<table><tr><td>id:</td><td>' +doc._id+ \
    '</td><tr><tr><td>type:</td><td>'+doc.type+ \
    '</td></tr><tr><tr><td>name:</td><td>'+doc.name+ \
    '</td></tr><tr><td>last_update:</td><td>'+Date(doc.last_update)+ 
    '</td></tr></table>'} else {return '<h4>Für die angegebene ID' +req.id+ \
    ' gibt es kein Ergebnis</h4>'}}"
}
          
curl -X GET http://localhost:5984/map-reduce/_design/simple/_show/ \
  html/f165fc62ed5d14ca84128de1d6034a36
<table><tr><td>id:</td><td>f165fc62ed5d14ca84128de1d6034a36</td><tr><tr><td>type:</td><td>document_store</td></tr><tr><tr><td>name:</td><td>CouchDB</td></tr><tr><td>last_update:</td><td>Tue May 08 2012 21:38:03 GMT+0200 (CEST)</td></tr></table>
          

_list functions

 

basic methods

start() set options for output - e.g a Header
getRow() content of one document
send() generate the output

_list functions - example

  "lists": {
    "html": "function(head, req){ \
      var row, table = ''; \
      start({'headers':{'content-type':'text/html'}}); \
      table += \
        '<table width=\"300\"><tr><td><b>type:</b></td><td><b>name:</b></td></tr>'; \
      while(row=getRow()) { \
        table += '<tr><td>' +row.key+ '</td><td>' +row.value+ '</td></tr>'; \
      }; \
      send(table + '</table>'); \
    }"
  },
          

_list functions - example - results

 

_list functions - example - even more results

 

URL-Rewriting

URL-Rewriting - in the _design document

       
"rewrites": [
  {
    "from": "/all",
    "to": "_list/html/all_database_types",
    "method": "GET",
    "query": {
       "reduce": "false"
    }
  },
  {
    "from": "/document_store",
    "to": "_list/html/all_database_types",
    "method": "GET",
    "query": {
       "reduce": "false",
       "key": "document_store"
    }
  },
  ...
],
          

URL-Rewriting - in the _design document

Results:

http://localhost:5984/map-reduce/_design/simple/_rewrite/all
http://localhost:5984/map-reduce/_design/simple/_rewrite/document_store
http://localhost:5984/map-reduce/_design/simple/_rewrite/document_store
http://localhost:5984/map-reduce/_design/simple/_rewrite/document_store_grouped
http://localhost:5984/map-reduce/_design/simple/_rewrite/last_update
http://localhost:5984/map-reduce/_design/simple/_rewrite/last_update_level_2

Q.E.D.

Virtual-Hosts

Virtual-Hosts

Find the configuration files:

couchdb -c
/usr/local/etc/couchdb/default.ini
/usr/local/etc/couchdb/local.ini
          
search for:
[vhosts]
;example.com = /database/
          

Virtual-Hosts - settings

[vhosts]
;example.com = /database/
map-reduce:5984 = /map-reduce/
          

restart CouchDB

curl -X GET http://map-reduce:5984/
{"db_name":"map-reduce","doc_count":13,"doc_del_count":0,
 "update_seq":69,"purge_seq":0,"compact_running":false,
 "disk_size":282728,"data_size":5544, 
 "instance_start_time":"1336516869140172",
 "disk_format_version":6,"committed_update_seq":69}
          

don't forget to set '127.0.0.1 map-reduce' in /etc/hosts!

Virtual-Hosts and URL Rewriting combined

change local.ini to:

[vhosts]
;example.com = /database/
map-reduce:5984 = /map-reduce/
map-reduce:5984/stats/ = /map-reduce/_design/simple/_rewrite/
          

now fire http://map-reduce:5984/stats/all

and

RELAX!

00:15 min break ...