couchdb – a database for the web
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
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
Schema-Fre
e
Document
Oriented
JSON
Distribute
d
RESTful
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
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!