CouchDB – A Database for the Web

Post on 29-Jan-2015

114 views 3 download

Tags:

description

http://webexpo.net/talk/couchdb-database-for-the-web/

transcript

CouchDB — A Database for the WebKarel Minařík

CouchDB — A Database for the Web

Karel Minařík

→ Independent web designer and developer

→ Ruby, Rails, Git and CouchDB propagandista in .cz

→ Previously: Flash Developer; Art Director; Information Architect;… (see LinkedIn)

→ @karmiq at Twitter

→ karmi.cz

CouchDB — A Database for the Web

$ couchdb

Apache CouchDB has started. Time to relax.

CouchDB — A Database for the Web

Apache CouchDB is a distributed, fault-tolerant

and schema-free document-oriented database

accessible via a RESTful HTTP/JSON API.

http://wiki.apache.org/couchdb

CouchDB — A Database for the Web

➡ The NoSQL Moniker➡ The CouchDB Story➡ Schema-free Document Storage➡ HTTP From Top to Bottom➡ RESTful API➡ Querying With Map/reduce➡ Fault-tolerant, Distributed, Highly-available and Concurrent➡ Demo: Example Application (Address Book)

Talk Outline

Project Voldemort

CouchDB — A Database for the Web

Reasons for NoSQLNOSQL

NoSQL is neither a “protest movement” nor “trendy bullshit”.

Reasons for developing new databases are real.Most stem from some real pain.

CouchDB — A Database for the Web

Database denormalization at DiggNOSQL

“Non-relational data stores reverse this model completely, because they don’t have the complex read operations of SQL. The model forces you to shift your computation to the writes, while reducing most reads to simple operations – the equivalent of SELECT * FROM `Table`.“http://about.digg.com/blog/looking-future-cassandra

SELECT `digdate`, `id` FROM `Diggs`WHERE `userid` IN (1, 2, 3, 4, ... 1000000)AND itemid = 123 ORDER BY `digdate` DESC, `id` DESC;

“A full query can actually clock in at 1.5kb, which is many times larger than the actual data we want. With a cold cache, this query can take 14 seconds to execute.”

CouchDB — A Database for the Web

Redis: Big O Notation Built–InNOSQL

redis> rpush mylist 1

redis> rpush mylist 2

redis> lpop mylist

"1"

...

redis> llen mylist

(integer) 1000000

redis> lpop mylist

"2"

$ redis-benchmark...====== LPOP ======10025 requests completed in 0.53 seconds...93.43% <= 3 milliseconds

CouchDB — A Database for the Web

Use Case: Job QueueNOSQL

RPUSH

LPOPO(1)

http://github.com/defunkt/resque/blob/master/lib/resque.rb#L133-138

}Millions of items

CouchDB — A Database for the Web

The CouchDB Story1

CouchDB — A Database for the Web

Damien Katz: CouchDB and MeTHE COUCHDB STORY

http://www.infoq.com/presentations/katz-couchdb-and-me

Damien Katz(RubyFringe 2008)

CouchDB — A Database for the Web

Damien Katz: CouchDB and MeTHE COUCHDB STORY

In the beginning, there was C++, XML and custom query language.

Stuff nobody ever got fired for.

Then came Erlang, HTTP, JSON and map/reduce.

CouchDB — A Database for the Web

Schema–free Documents2

CouchDB — A Database for the Web

“Relational Data”SCHEMA-FREE STORAGE

OH: “The world is relational!!!”

17 minutes ago via Tweetie for MacRetweeted by 10000 people

CouchDB — A Database for the Web

That does not mean the world conforms to the third normal form.

CouchDB — A Database for the Web

In fact, it’s rather the exact opposite.

CouchDB — A Database for the Web

The Textbook ExampleSCHEMA–FREE DOCUMENTS

Design a customer database.People have names, e-mail, phone numbers, …

How many phone numbers?

CouchDB — A Database for the Web

The Textbook ExampleSCHEMA–FREE DOCUMENTS

Customers

id INTEGER PNA

first_name VARCHAR

last_name VARCHAR

phone VARCHAR

Now. What about multiple phone numbers?

Relational Databases 101

http://en.wikipedia.org/wiki/First_normal_form#Domains_and_values

CouchDB — A Database for the Web

The Textbook ExampleSCHEMA–FREE DOCUMENTS

“We will use the database only from the application, anyway.”

Customers

id INTEGER PNA

first_name VARCHAR

last_name VARCHAR

phone VARCHAR

The “solution”, Pt. 1

http://en.wikipedia.org/wiki/First_normal_form#Domains_and_values

CouchDB — A Database for the Web

The Textbook ExampleSCHEMA–FREE DOCUMENTS

“This is clearly better design!”

Alright. Then, please answer these questions:

• How do you search for a customers given a phone number?• Which customers have the same phone number?• How many phone numbers a customer has?

Then, please add the ability to store four phone numbers. Thanks.

The “solution”, Pt. 2

Customers

id INTEGER PNA

first_name VARCHAR

last_name VARCHAR

phone_1 VARCHAR

phone_2 VARCHAR

phone_3 VARCHAR

http://en.wikipedia.org/wiki/First_normal_form#Domains_and_values

CouchDB — A Database for the Web

The Textbook ExampleSCHEMA–FREE DOCUMENTS

The Right Solution

Customers

id INTEGER PNAU

first_name VARCHAR

last_name VARCHAR

CustomerPhones

customer_id INTEGER FNi

phone VARCHAR N

http://en.wikipedia.org/wiki/First_normal_form#Domains_and_values

CouchDB — A Database for the Web

The Textbook ExampleSCHEMA–FREE DOCUMENTS

mysql> SELECT * FROM Customers LEFT JOIN CustomerPhones                               ON Customers.id = CustomerPhones.customer_id;

+‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+| id | first_name | last_name | customer_id | phone |+‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+|  1 | John       | Smith     | 1           | 123   | |  1 | John       | Smith     | 1           | 456   | +‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+

CouchDB — A Database for the Web

The Textbook ExampleSCHEMA–FREE DOCUMENTS

mysql> SELECT * FROM Customers WHERE id = 1;

+‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐+| id | first_name | last_name |+‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐+|  1 | John       | Smith     | +‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐+

mysql> SELECT phone FROM CustomerPhones WHERE customer_id IN (1);

+‐‐‐‐‐‐‐+| phone |+‐‐‐‐‐‐‐+| 123   | | 456   | +‐‐‐‐‐‐‐+

CouchDB — A Database for the Web

Structured dataSCHEMA–FREE DOCUMENTS

But, damn!, I want something like this:

{  "id"         : 1,

  "first_name" : "Clara",  "last_name"  : "Rice",

  "phones"     : ["0000 777 888 999", "0000 123 456 789", "0000 314 181 116"]}

“No problem, you just iterate over the rows and build your object. That’s the way it is!”

“If this would be too painful, we will put some cache there.”

CouchDB — A Database for the Web

Ephemeral dataSCHEMA–FREE DOCUMENTS

class User < ActiveRecord::Base

  serialize :preferences

end

Not everything needs to be done “right”. Right?

CouchDB — A Database for the Web

“Consistency”SCHEMA–FREE DOCUMENTS

Does the “Right Way“ sometimes fail?Hell yeah.

EXAMPLE

When designing an invoicing application, you store the customer for the invoice the “right way”, via foreign keys.

Then, the customer address changes.

Did the address on the invoice also changed?

CouchDB — A Database for the Web

cityreality, ltd.

123 EVERYWHERE AVENUE CITY, ST 00000

Phone: 555.555.5555

Fax: 444.444.4444

Skype: city.reality.ltd

Documents in the Real WorldSCHEMA–FREE DOCUMENTS

1 2 3 F a c t o r y S t r e e t C i t y , S t 0 0 0 0 0

design things

UNDERGROUND RECORDS

info@undergroundrecords.com

F: 555.555.5555

P: 555.555.5555M: 777.777.7777M: 888.000.1111

123 BOULEVARDAVE, SUITE 000LOS ANGELES, U.S.A/

www.undergroundrecor

ds.com

http://guide.couchdb.org/draft/why.html#better

CouchDB — A Database for the Web

Documents in the Real WorldSCHEMA–FREE DOCUMENTS

{ "_id" : "clara-rice", "_rev" : "1-def456",

"first_name" : "Clara", "last_name" : "Rice",

"phones" : { "mobile" : "0000 777 888 999" "home" : "0000 123 456 789", "work" : "0000 314 181 116" },

"addresses" : { "home" : { "street" : "Wintheiser Ports", "number" : "789/23", "city" : "Erinshire", "country" : "United Kingdom" }, },

"occupation" : "model", "birthday" : "1970/05/01",

"groups" : ["friends", "models"],

"created_at" : "2010/01/01 10:00:00 +0000"}

JSON

CouchDB — A Database for the Web

Documents in the Real WorldSCHEMA–FREE DOCUMENTS

CouchDB — A Database for the Web

Procrustean Bed

CouchDB — A Database for the Web

RESTful HTTP3

1990s

CouchDB — A Database for the Web

Django may be built for the Web,but CouchDB is built of the Web.

I’ve never seen soware that so completely embraces the philosophies behind HTTP.Jacob Kaplan-Moss, Of the Web (2007)

http://jacobian.org/writing/of-the-web/

HTTP

Built “Of the Web”

CouchDB — A Database for the Web

CouchDB makes Django look old-school in the same way that Django makes ASP look outdated.

HTTP

Built “Of the Web”

http://jacobian.org/writing/of-the-web/

CouchDB — A Database for the Web

HTTP is the lingua anca of our age; if you speak HTTP, it opens up all sorts of doors.

ere’s something almost subversive about CouchDB; it’s completely language-, platform-, and OS-agnostic.

Built “Of the Web”HTTP

http://jacobian.org/writing/of-the-web/

CouchDB — A Database for the Web

HOST=http://localhost:5984

curl ‐X GET $HOST# {"couchdb":"Welcome","version":"0.11.0b22c551bb‐git"}

curl ‐X GET $HOST/my‐database# {"error":"not_found","reason":"no_db_file"}

curl ‐X PUT $HOST/my‐database# {"ok":true}

curl ‐X PUT $HOST/my‐database/abc123 ‐d '{"foo":"bar"}'# {"ok":true,"id":"abc123","rev":"1‐4c6114c65e295552ab1019e2b046b10e"}

curl ‐X GET $HOST/my‐database/abc123# {"_id":"abc123","_rev":"1‐4c6114c65e295552ab1019e2b046b10e","foo":"bar"}

curl ‐X DELETE $HOST/my‐database/abc123?rev=2‐d179f665eb01834faf192153dc72dcb3# {"ok":true,"id":"abc123","rev":"1‐4c6114c65e295552ab1019e2b046b10e"}

HTTP APIHTTP

CouchDB — A Database for the Web

require 'rubygems'require 'ostruct'

require 'restclient'

require 'json'

class Article < OpenStruct

  def self.db(path='')    RestClient::Resource.new "http://localhost:5984/blog/#{path}",                             :headers => { :content_type => :json, :accept => :json }  end

  db.put '' rescue RestClient::PreconditionFailed

  def self.create(params={})    new db.post(params.to_json)  end

  def self.find(id)    new JSON.parse( db(id).get )  end

  def destroy    self.class.db(self._id + "?rev=#{self._rev}").delete  end end

Easy To WrapHTTP

HTTP libraryJSON library

12

CouchDB — A Database for the Web

Article.create :_id   => 'my‐first‐post',               :title => 'CouchDB is easy',               :body  => 'So relax!',               :tags  => ['couchdb', 'databases'] rescue RestClient::Conflict

article =  Article.find('my‐first‐post')

puts "Got an article:"p article

puts "\n‐‐‐‐‐‐"puts "Title: %s" % article.title        + "     (class: #{article.title.class})"puts "Tags:  %s" % article.tags.inspect + "     (class: #{article.tags.class})"puts "‐‐‐‐‐‐\n\n"

puts "Deleting article..."article.destroy

Easy To WrapHTTP

CouchDB — A Database for the Web

$ curl ‐X POST http://localhost:5984/_replicate \       ‐d '{"source":"database",             "target":"http://example.org/database"}'

HTTP from Top to BottomHTTP

CouchDB — A Database for the Web

$ curl ‐i ‐X GET $HOST/my‐database/abc123

HTTP/1.1 200 OKServer: CouchDB/1.0.1 (Erlang OTP/R14B)Etag: "4‐f04f2435e031054d6b5298c5841ae052"Date: Thu, 23 Sep 2010 12:56:37 GMTContent‐Type: text/plain;charset=utf‐8Content‐Length: 73Cache‐Control: must‐revalidate

{"_id":"abc123","_rev":"4‐f04f2435e031054d6b5298c5841ae052","foo":"bar"}

Making Real Use of HTTPHTTP

$ cat /etc/squid3/squid.conf

cache_peer 192.168.100.2 parent 5984 0 no‐query originserver name=masteracl master_acl method GET POST PUT DELETEcache_peer_access master allow master_acl

CouchDB — A Database for the Web

REST is a set of principles that define how Web standards, such as HTTP and URIs, are supposed to be used. (...) In summary, the five key principles are:

➡ Give every “thing” an ID➡ Link things together➡ Use standard methods➡ Resources with multiple representations➡ Communicate statelessly

Stefan Tilkov, A Brief Introduction to REST

What is “RESTful”?HTTP

http://www.infoq.com/articles/rest-introduction

CouchDB — A Database for the Web

What is “RESTful”?HTTP

The basic idea is even more simple, though.

HTTP is not just a “transfer protocol”.

It is the interface for interacting with “things” itself.

CouchDB — A Database for the Web

Fault-Tolerant and Concurrent4

CouchDB — A Database for the Web

$ kill ‐9 <PID>

CouchDB has no off switch.CouchDB has no repair command.

CouchDB — A Database for the Webhttp://www.youtube.com/watch?v=uKfKtXYLG78

Erlang!

ErlangFAULT–TOLERANT

CouchDB — A Database for the Web

Erlang's main strength is support for concurrency. It has a small but powerful set of primitives to create processes and communicate among them.(…) a benchmark with 20 million processes has been successfully performed.

ErlangFAULT–TOLERANT

http://en.wikipedia.org/wiki/Erlang_(programming_language)

CouchDB — A Database for the Webhttp://guide.couchdb.org/draft/btree.html

Append–Only B–TreeFAULT–TOLERANT

CouchDB — A Database for the Web

Querying With Map/reduce5

CouchDB — A Database for the Web

The Google PaperMAP/REDUCE

http://labs.google.com/papers/mapreduce.html

CouchDB — A Database for the Web

The ConceptMAP/REDUCE

module Enumerable  alias :reduce :inject unless method_defined? :reduceend

(1..3).map    { |number| number * 2 }# => [2, 4, 6]

(1..3).reduce(0) { |sum, number| sum += number}# => 6

CouchDB — A Database for the Web

function(doc) {  if (doc.last_name && doc.first_name) {    emit( doc.last_name + ' ' + doc.first_name, doc )  }}

The Simplest ViewMAP/REDUCE

CouchDB — A Database for the Web

function(doc) {  if (doc.last_name && doc.first_name) {    emit( doc.last_name + ' ' + doc.first_name, doc )  }}

The Simplest ViewMAP/REDUCE

INPUT

OUTPUT KEY VALUE

CouchDB — A Database for the Web

The Result of MapMAP/REDUCE

Key Value

"Armstrong Lottie"

_id: "lottie‐armstrong",_rev: "2‐fcb71b26096957b3ff3ffd2970f3c933",addresses: {  home: {  city: "Murphyville"  ...  }},first_name: "Lottie",last_name: "Armstrong",occupation: "programmer",

"Bailey Kaelyn"

_id: "kaelyn‐bailey",_rev: "1‐2e25e6c9448520fa796988894423a23b",addresses: {      home: {        city: "Lake Dedric"        ...      }},first_name: "Kaelyn",last_name: "Bailey",occupation: "supermodel"

... ...

CouchDB — A Database for the Web

The Result of MapMAP/REDUCE

CouchDB — A Database for the Web

function(doc) { emit(doc.occupation, 1);}

Even Simpler ViewMAP/REDUCE

CouchDB — A Database for the Web

Result of Even Simpler ViewMAP/REDUCE

http://localhost:5984/_utils/database.html?addressbook/_design/person/_view/by_occupation

CouchDB — A Database for the Web

function(keys, values) {  return sum(values)}

A Simple ReduceMAP/REDUCE

CouchDB — A Database for the Web

Result of a Simple ReduceMAP/REDUCE

http://localhost:5984/_utils/database.html?addressbook/_design/person/_view/by_occupation

CouchDB — A Database for the Web

Built–In Erlang Reduce functionsMAP/REDUCE

http://wiki.apache.org/couchdb/Built-In_Reduce_Functions#Available_Build-In_Functions

_count_sum_stats

$ couchdb

Apache CouchDB has started. Time to relax.

CouchDB — A Database for the Web

function(doc) {  for (group in doc.groups) {    emit(doc.groups[group], 1)  }}

_count

Map/Reduce for Counting “tag-like stuff”MAP/REDUCE

CouchDB — A Database for the Web

Result of the Map phaseMAP/REDUCE

http://localhost:5984/_utils/database.html?addressbook/_design/person/_view/by_groups

CouchDB — A Database for the Web

Result of the Reduce PhaseMAP/REDUCE

http://localhost:5984/_utils/database.html?addressbook/_design/person/_view/by_groups

CouchDB — A Database for the Web

function(doc) {  var date = new Date(doc.birthday)  emit( [date.getFullYear(), date.getMonth()+1, date.getDate()], 1 )}

_count

Group LevelsMAP/REDUCE

COMPOSITE KEY (ARRAY)

CouchDB — A Database for the Web

Group Level ExactMAP/REDUCE

http://localhost:5984/_utils/database.html?addressbook/_design/person/_view/by_birthday

CouchDB — A Database for the Web

Group Level 2MAP/REDUCE

http://localhost:5984/_utils/database.html?addressbook/_design/person/_view/by_birthday

CouchDB — A Database for the Web

Group Level 1MAP/REDUCE

http://localhost:5984/_utils/database.html?addressbook/_design/person/_view/by_birthday

CouchDB — A Database for the Web

key

startkey

startkey_docid

endkey

endkey_docid

limit

stale

descending

skip

group

group_level

reduce

include_docs

Parameters for querying viewsQUERYING VIEWS

CouchDB — A Database for the Web

A Complex Map/ReduceQUERYING VIEWS

CouchDB — A Database for the Web

A Complex Map/ReduceQUERYING VIEWS

SELECT

      COUNT(*) AS count,        DATE_FORMAT(published_at, "%Y/%m/%d") AS date,

        keywords.value AS keyword      FROM feed_entries        INNER JOIN feeds ON feed_entries.feed_id = feeds.id

        INNER JOIN keywords ON feeds.keyword_id = keywords.id      WHERE DATE_SUB(CURDATE(), INTERVAL 90 DAY) <= feed_entries.published_at

      GROUP BY date, keyword      ORDER BY date, keyword ASC;

CouchDB — A Database for the Web

A Complex Map/ReduceQUERYING VIEWS

Streamgraph.load_data({  max : 170,

  keywords : ['ruby', 'python', 'erlang', 'javascript', 'haskell'],

  values   : [    { date: '2010/01/01', ruby: 50,  python: 20, erlang: 5,  javascript: 30, haskell: 50 },    { date: '2010/02/01', ruby: 20,  python: 20, erlang: 2,  javascript: 40, haskell: 43 },    { date: '2010/03/01', ruby: 70,  python: 20, erlang: 10, javascript: 80, haskell: 15 },    { date: '2010/04/01', ruby: 20,  python: 40, erlang: 8,  javascript: 30, haskell: 12 },    { date: '2010/05/01', ruby: 150, python: 30, erlang: 12, javascript: 40, haskell: 18 },    { date: '2010/06/01', ruby: 30, python: 10, erlang: 14, javascript: 170, haskell: 14 }  ]

});

But. We don’t need a table. We need the data in a format like this:

CouchDB — A Database for the Web

The Map PhaseQUERYING VIEWS

function(doc) {  var fix_date = function(junk) {      var formatted = junk.toString().replace(/‐/g,"/").replace("T"," ").substring(0,19);      return new Date(formatted);    };  // Format integers to have at least two digits.  var f = function(n) { return n < 10 ? '0' + n : n; }  // This is a format that collates in order and tends to work with  // JavaScript's new Date(string) date parsing capabilities, unlike rfc3339.  Date.prototype.toJSON = function() {      return this.getUTCFullYear()   + '/' +           f(this.getUTCMonth() + 1) + '/' +           f(this.getUTCDate())      + ' ' +           f(this.getUTCHours())     + ':' +           f(this.getUTCMinutes())   + ':' +           f(this.getUTCSeconds())   + ' +0000';  };  if (doc['couchrest‐type'] == 'Mention') {    for ( keyword in doc.keywords ) {      var key = fix_date(doc.published_at).toJSON().substring(0,10);      var value = {};      value[ doc.keywords[keyword] ] = 1;      emit( key, value);    }  }}

CouchDB — A Database for the Web

The Reduce PhaseQUERYING VIEWS

function(keys, values, rereduce) {  if (rereduce) {    var result = {}    for ( item in values ) {      for (prop in values[item]) {        if ( result[prop] ) { result[prop] += values[item][prop]  }        else { result[prop] = values[item][prop]  }      }    }    return result;  }  else {    // Prepare the data for the re‐reduce    var date   = keys[0][0];    var result = {}    for (value in values) {      var item = values[value];      for (prop in item) {        if ( result[prop] ) { result[prop] += item[prop] }        else { result[prop] = item[prop] }      }    }    return result;  }}

CouchDB — A Database for the Web

The ResultQUERYING VIEWS

{    "rows": [        {            "key": "2010/09/22",            "value": { "ruby": 8, "python": 19 }        },        {            "key": "2010/09/23",            "value": { "ruby": 24, "python": 12 }         },        {            "key": "2010/09/24",            "value": { "ruby": 7, "python": 8 }        }    ]}

$ curl http://localhost:5984/customer_database/_design/Mention/_view/by_date_and_keyword?group=true

CouchDB — A Database for the Web

I ♥JS. Or... don’t?QUERYING VIEWS

CouchDB — A Database for the Web

Complex QueriesQUERYING VIEWS

So… What if you need something like:

Show me all supermodels who live in Beckerborough.

Out of luck?

CouchDB — A Database for the Web

CouchDB–LuceneCOMPLEX QUERIES

Show me all supermodels who live in Beckerborough.

This guy knows.

CouchDB — A Database for the Webhttp://github.com/rnewson/couchdb-lucene

Couchdb-Lucene.When you need foo AND bar.

foo AND barCOMPLEX QUERIES

CouchDB — A Database for the Web

function(doc) {

  var result = new Document();

  if (doc.occupation)     {    result.add(doc.occupation,                    {"field":"occupation"})  }

  if (doc.addresses)       {    for (address in doc.addresses) {      result.add(doc.addresses[address].city,     {"field":"city"})    }  }

  return result;

}

Indexing functionCOUCHDB-LUCENE

http://localhost:5984/addressbook/_fti/_design/person/search?q=occupation:supermodel AND city:Beckerborough

CouchDB — A Database for the Web

Distributed6

CouchDB — A Database for the Web

Ubuntu OneDISTRIBUTED

CouchDB — A Database for the Web

ReplicationDISTRIBUTED

CouchDB — A Database for the Web

Conflict ResolutionsDISTRIBUTED

http://guide.couchdb.org/draft/consistency.html#study

_rev1

CouchDB — A Database for the Webhttp://ephemera.karmi.cz/post/247255194/simple-couchdb-multi-master-clustering-via-nginx

Simple Clustering With HTTP Reverse ProxiesDISTRIBUTED

CouchDB — A Database for the Web

Scaling DownDISTRIBUTED

http://www.couchone.com/page/android

CouchDB — A Database for the Web

CouchAppsDISTRIBUTED

CouchDB — A Database for the Web

CouchAppsDISTRIBUTED

http://pollen.nymphormation.org/afgwar/_design/afgwardiary/index.html

CouchDB — A Database for the Web

CouchAppsDISTRIBUTED

CouchDB — A Database for the Web

Resources7

CouchDB — A Database for the Web

ResourcesDISTRIBUTED

➡ http://guide.couchdb.org

➡ https://nosqleast.com/2009/#speaker/miller

➡ http://www.couchone.com/migrating-to-couchdb

➡ http://wiki.apache.org/couchdb/

➡ http://blog.couchone.com/

➡ http://stackoverflow.com/tags/couchdb/

CouchDB — A Database for the Web

Demo: Example Application8

CouchDB — A Database for the Web

ApplicationDEMO

http://karmi.couchone.com/addressbook/_design/person/_list/all/all

SOURCE CODE: http://github.com/karmi/couchdb-showcase

Questions!