CouchDB has a lot of documentation in its wiki. A good overview of how to access it directly through HTTP is at this wiki page. When starting learning CouchDB I found it quite succinct. This is great after you are “in business” and just need to confirm something or refresh your memory, but I think it’s hard for the first couple of sessions.
The below sample is a sample interaction that shows:
- Using pure curl access – the basics
- The following operations:
- Creating a database
- Deleting a database
- Creating documents
- Creating design documents
- Which implies updating documents, as design documents are nothing but regular documents with a special name
- Creating views
- Updating views
- Getting view results
- Python curl is used for simplicity – this could have been done through command line curl or using some other programming language
- Actual HTTP calls if enabled via
- conn.set_debuglevel(1) setting (see code)
- Most importantly – how much easier is to use some of the libraries (such as these) that wrap all this in simpler calls, after you grasp the basics
Some important notes:
- Be sure to change the parameters at the start of the script if they are different
- Important: this is destructive – it will ruin the given database, so be careful
- For the above reason, I put the ‘raise Exception’ statement – comment it out to run the code
Here’s the code:
#!/usr/bin/python # License: MIT (http://www.opensource.org/licenses/mit-license.php) # # Copyright (c) 2011. icyrock.com # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # -------------------------------------------- # Sample plain HTTP Python <-> CouchDB session # -------------------------------------------- # # This sample python script is a small curl-based interaction with # CouchDB. When you run it, you should be able to see what you can # actually do using low-level curl interface that CouchDB supports. If # you uncomment the following line: # # #conn.set_debuglevel(1) # # you will see pure HTTP calls that are actually being made. Looking at # these you should get a better understanding about how CouchDB works # if you take out all the libraries that you might use and which cover # the details of what actually happens beneath. # # Of course, after you have this all figured out, go and use the # library - it's easier that way. Take a look at this page for more # info: http://wiki.apache.org/couchdb/Getting_started_with_Python import httplib import urllib2 import json from textwrap import dedent # Some defaults, change to match your system # !!! Note the database dbname will be destroyed !!! base_url = 'localhost:5984' dbname = 'robocouch' docprefix = 'rcd' raise Exception('Comment out after confirming the above parameters') # Some helpers class PropertyDict(dict): def __init__(self, **items): self.__dict__.update(items) for k in items: self[k] = items[k] conn = httplib.HTTPConnection(base_url) # If you want to see the actual HTTP calls made by httplib, uncomment the below # Very useful to grasp the low level stuff #conn.set_debuglevel(1) def couchdb_http_action(method, url, body=None): if body != None: json_send = json.dumps(body) headers = {'Content-Type': 'application/json'} conn.request(method, '/%s' % url, json_send, headers) else: conn.request(method, '/%s' % url) resp = conn.getresponse() raw = resp.read() info = PropertyDict( status=resp.status, raw=raw, data=json.loads(raw), ) return info # Helper methods - these basically do a HTTP call as given the name # For example, hget = HTTP GET def hget(url): return couchdb_http_action('GET', url) def hpost(url, data): return couchdb_http_action('POST', url, data) def hdelete(url): return couchdb_http_action('DELETE', url) def hput(url, data=None): return couchdb_http_action('PUT', url, data) def pretty(data): # return json.dumps(data, sort_keys=True, indent=2) return json.dumps(data, indent=2) # # Let's get to work # print('Welcome!') # Create our database, delete if currently existing print('Our database: %s' % dbname) alldbs = hget('_all_dbs').data print('All databases: %s' % alldbs) if dbname in alldbs: print('Our database %s is present, deleting' % dbname) hdelete(dbname) print('Deleted %s, all databases: %s' % (dbname, hget('_all_dbs').data)) hput(dbname) print('Created database %s, all databases: %s' % (dbname, hget('_all_dbs').data)) # Create some documents for i in range(1, 6): docid = '%s%d' % (docprefix, i) print('Creating document %s' % docid) hput('%s/%s' % (dbname, docid), dict(number=i*33)) #print('All document IDs in %s: %s' % (dbname, [row['id'] for row in hget('%s/_all_docs' %dbname).data['rows']])) print('All document IDs in %s: %s' % (dbname, hget('%s/_all_docs' %dbname).raw)) # Create a design document with the same name as our database (defined by dbname variable) # It will contain a single view called 'all_numbers' # This view will have only a map part and will return all documents with 'number' attribute hput('%s/_design/%s' % (dbname, dbname), dict( language='javascript', views=dict( all_numbers=dict( map=dedent(''' function(doc) { if(doc.number) { emit(null, doc) } } ''') ), ) )) # Actually run the view print("All documents with 'number' attribute:") #print(pretty(hget('%s/_design/%s/_view/all_numbers' % (dbname, dbname)).data)) print(hget('%s/_design/%s/_view/all_numbers' % (dbname, dbname)).raw) # Update the design document by adding another view called count_sum_numbers # Since this is an update, we need to fetch to get the current revision of the document # The revision is part of our JSON outbound request # This one will sum all documents which have 'number' attribute and give us their count # Note that the design document is a whole, thus our previous view needs to be preserved rev = hget('%s/_design/%s' % (dbname, dbname)).data['_rev'] print('Updating revision %s' % rev) hput('%s/_design/%s' % (dbname, dbname), dict( _rev=rev, language='javascript', views=dict( all_numbers=dict( map='function(doc) { if(doc.number) emit(null, doc) }', ), count_sum_numbers=dict( map='function(doc) { if(doc.number) emit(null, doc.number) }', reduce=dedent(''' function(key, values, rereduce) { var sum = 0 var count = 0 for(var i = 0; i < values.length; i++) { sum += values[i] count += rereduce ? values.count : 1 } return {count:count, sum:sum} } ''') ), ) )) # Get the sum of all numbers # Rows contains one row per key. Since we had only one (null) key, we have only one row res = hget('%s/_design/%s/_view/count_sum_numbers' % (dbname, dbname)).data['rows'][0]['value'] print('Sum of all %d available numbers: %d' % (res['count'], res['sum']))