Package couchdb3
CouchDB3
CouchDB3 is a wrapper around the CouchDB API. For more detailed information, please refer to the documentation.
Disclaimer
Big parts of the documentation (and thus docstrings) have been copied from CouchDB's API's great official documentation.
Requirements
- Python version
>= 3.11 - CouchDB version
3.x
Installation
Installing via PyPi
pip install couchdb3
Installing via Github
python -m pip install git+https://github.com/n-Vlahovic/couchdb3.git
Installing from source
git clone https://github.com/n-Vlahovic/couchdb3
python -m pip install -e couchdb3
Import styles
All public classes are available flat from couchdb3 for backward compatibility.
Explicit subpackage imports are also supported for clarity:
# Flat (backward-compatible)
from couchdb3 import Server, AsyncServer, Document
# Explicit sync subpackage
from couchdb3.sync import Server, Database, Partition
# Explicit async subpackage
from couchdb3.aio import AsyncServer, AsyncDatabase, AsyncPartition
# Shared types always from couchdb3 directly
from couchdb3 import Document, ViewResult, ViewRow, exceptions
Quickstart — Sync
Connecting to a database server
import couchdb3
client = couchdb3.Server(
"http://user:password@127.0.0.1:5984"
)
# Checking if the server is up
print(client.up())
# True
User and password can also be passed as keyword parameters:
client = couchdb3.Server(
"127.0.0.1:5984", # Scheme omitted - will assume http protocol
user="user",
password="password"
)
Both approaches are equivalent. Clients can also be used as context managers:
with couchdb3.Server("http://user:password@127.0.0.1:5984") as client:
# Do stuff
...
Getting or creating a database
dbname = "mydb"
db = client.get(dbname) if dbname in client else client.create(dbname)
print(db)
# Database: mydb
Creating a document
mydoc = {
"_id": "mydoc-id",
"name": "Hello",
"type": "World"
}
print(db.save(mydoc))
# ('mydoc-id', True, '1-24fa3b3fd2691da9649dd6abe3cafc7e')
Note: Database.save requires the document to have an id (i.e. a key _id),
Database.create does not.
Updating a document
To update an existing document, retrieving the revision is paramount.
In the example below, dbdoc contains the key _rev and the builtin dict.update function is
used to update the document before saving it.
mydoc = {
"_id": "mydoc-id",
"name": "Hello World",
"type": "Hello World"
}
dbdoc = db.get(mydoc["_id"])
dbdoc.update(mydoc)
print(db.save(dbdoc))
# ('mydoc-id', True, '2-374aa8f0236b9120242ca64935e2e8f1')
Alternatively, one can use Database.rev to fetch the latest revision and overwrite the document:
mydoc = {
"_id": "mydoc-id",
"_rev": db.rev("mydoc-id"),
"name": "Hello World",
"type": "Hello World"
}
print(db.save(mydoc))
# ('mydoc-id', True, '3-d56b14b7ffb87960b51d03269990a30d')
Deleting a document
To delete a document, the docid and rev are needed:
docid = "mydoc-id"
print(db.delete(docid=docid, rev=db.rev(docid))) # Fetch the revision on the go
# True
Fetching documents
# Fetch a single document (returns None if not found)
doc = db.get("mydoc-id")
# Subscript shorthand (raises KeyError if not found)
doc = db["mydoc-id"]
# Fetch all documents (metadata only)
result = db.all_docs() # ViewResult
# Fetch all documents with full bodies
result = db.all_docs(include_docs=True)
for row in result.rows:
print(row.id, row.doc)
# Fetch specific documents by ID
result = db.all_docs(keys=["id-1", "id-2"], include_docs=True)
# Batch fetch by ID
result = db.bulk_get(docs=[{"id": "id-1"}, {"id": "id-2"}])
for item in result:
print(item["id"], item["docs"][0]["ok"])
Views
# 1. Create a design document with a map function
db.put_design("my-ddoc", views={
"my-view": {
"map": "function(doc) { if (doc.type === 'post') emit(doc._id, null); }"
}
})
# 2. Query the view
result = db.view("my-ddoc", "my-view") # ViewResult
result = db.view("my-ddoc", "my-view", include_docs=True, limit=10)
# 3. Iterate results
for row in result.rows:
print(row.id, row.key, row.value)
Mango queries
# 1. Create an index
db.save_index({"fields": ["type", "name"]}, ddoc="my-ddoc", name="type-name-idx")
# 2. Query with a selector
result = db.find({"type": {"$eq": "post"}}, fields=["_id", "name"], limit=10)
for doc in result["docs"]:
print(doc)
# 3. Inspect the query plan
plan = db.explain({"type": {"$eq": "post"}}, limit=10)
Working with partitions
For a partitioned database, the Partition class offers a wrapper around
partitions (acting similarly to collections in Mongo).
from couchdb3.sync import Server, Database, Partition
client: Server = Server(...)
db: Database = client["some-db"]
partition: Partition = db.get_partition("partition_id")
Partition instances prepend the partition's ID to document IDs (partition-id:doc-id)
for simpler user interaction:
doc_id = "test-id"
print(doc_id in partition) # no need to prepend the partition's ID
rev = partition.rev(doc_id)
partition.save({
"_id": doc_id, # no need to prepend the partition's ID
"_rev": rev,
...
})
The partition ID will only be prepended if the document ID does not already start with it:
doc_id = "partition_id:test-id"
print(doc_id in partition)
rev = partition.rev(doc_id)
partition.save({
"_id": doc_id,
"_rev": rev,
...
})
Quickstart — Async
Connecting to a database server
import asyncio
from couchdb3.aio import AsyncServer
async def main():
async with AsyncServer("http://user:password@127.0.0.1:5984") as client:
print(await client.up())
# True
asyncio.run(main())
Getting or creating a database
async with AsyncServer("http://user:password@127.0.0.1:5984") as client:
dbname = "mydb"
all_dbs = await client.all_dbs()
db = await client.get(dbname) if dbname in all_dbs else await client.create(dbname)
Creating and fetching documents
async with AsyncServer("http://user:password@127.0.0.1:5984") as client:
db = await client.get("mydb")
# Save a document
_id, ok, _rev = await db.save({"_id": "mydoc-id", "name": "Hello", "type": "World"})
# Fetch a document
doc = await db.get("mydoc-id")
print(doc)
# Delete a document
await db.delete(docid="mydoc-id", rev=await db.rev("mydoc-id"))
Async context manager (manual lifecycle)
from couchdb3.aio import AsyncServer
client = AsyncServer("http://user:password@127.0.0.1:5984")
db = await client.get("mydb")
doc = await db.get("mydoc-id")
await client.aclose() # must be called explicitly when not using async with
Sub-modules
couchdb3.aio-
Asynchronous CouchDB client subpackage …
couchdb3.documentcouchdb3.exceptionscouchdb3.sync-
Synchronous CouchDB client subpackage …
couchdb3.utilscouchdb3.view