0
# Document Operations
1
2
Complete CRUD operations for JSON documents with attachment support, conflict resolution, batch operations, and field-level update utilities.
3
4
## Capabilities
5
6
### Document Access
7
8
Access documents through dict-like interface on the database.
9
10
```python { .api }
11
class CouchDatabase(dict):
12
"""Dict-like interface for document access."""
13
14
def keys(self, remote=False):
15
"""
16
Get all document IDs in the database.
17
18
Parameters:
19
- remote (bool): Fetch from server (default: False, uses cache)
20
21
Returns:
22
list[str]: Document IDs
23
"""
24
25
def __getitem__(self, key):
26
"""
27
Access document by ID.
28
29
Parameters:
30
- key (str): Document ID
31
32
Returns:
33
Document: Document instance
34
"""
35
36
def get(self, key, remote=False):
37
"""
38
Safe document access.
39
40
Parameters:
41
- key (str): Document ID
42
- remote (bool): Fetch from server if not cached
43
44
Returns:
45
Document | None: Document instance or None if not found
46
"""
47
48
def __contains__(self, key):
49
"""
50
Check if document exists.
51
52
Parameters:
53
- key (str): Document ID
54
55
Returns:
56
bool: True if document exists
57
"""
58
59
def __iter__(self, remote=True):
60
"""
61
Iterate over all documents.
62
63
Parameters:
64
- remote (bool): Fetch from server (default: True)
65
66
Yields:
67
Document: Document instances
68
"""
69
```
70
71
### Document Creation
72
73
Create new documents in the database.
74
75
```python { .api }
76
class CouchDatabase(dict):
77
"""Document creation methods."""
78
79
def create_document(self, data, throw_on_exists=False):
80
"""
81
Create document with provided data.
82
83
Parameters:
84
- data (dict): Document data including optional _id
85
- throw_on_exists (bool): Raise exception if document exists
86
87
Returns:
88
Document: Created document instance
89
90
Raises:
91
CloudantDocumentException: Document creation failed
92
"""
93
94
def new_document(self):
95
"""
96
Create empty document with generated ID.
97
98
Returns:
99
Document: Empty document with auto-generated _id
100
"""
101
```
102
103
### Document Class
104
105
Core document operations with CRUD functionality.
106
107
```python { .api }
108
class Document(dict):
109
"""
110
Represents a CouchDB/Cloudant document with CRUD operations.
111
"""
112
113
def __init__(self, database, document_id=None, **kwargs):
114
"""
115
Initialize document instance.
116
117
Parameters:
118
- database (CouchDatabase | CloudantDatabase): Parent database
119
- document_id (str): Document ID (auto-generated if None)
120
- **kwargs: Initial document fields
121
"""
122
123
def exists(self):
124
"""
125
Check if document exists in database.
126
127
Returns:
128
bool: True if document exists on server
129
"""
130
131
def create(self):
132
"""
133
Create document in database.
134
135
Returns:
136
dict: Creation response with _id and _rev
137
138
Raises:
139
CloudantDocumentException: Document already exists or creation failed
140
"""
141
142
def fetch(self):
143
"""
144
Fetch latest document version from database.
145
146
Returns:
147
None: Document data is updated in-place
148
149
Raises:
150
CloudantDocumentException: Document not found or fetch failed
151
"""
152
153
def save(self):
154
"""
155
Save document changes to database.
156
157
Returns:
158
dict: Save response with updated _rev
159
160
Raises:
161
CloudantDocumentException: Conflict or save failed
162
"""
163
164
def delete(self):
165
"""
166
Delete document from database.
167
168
Returns:
169
dict: Deletion response
170
171
Raises:
172
CloudantDocumentException: Deletion failed
173
"""
174
175
def json(self):
176
"""
177
Get JSON representation of document.
178
179
Returns:
180
str: JSON string of document data
181
"""
182
183
@property
184
def document_url(self):
185
"""Document URL endpoint"""
186
187
@property
188
def r_session(self):
189
"""HTTP request session"""
190
```
191
192
### Field Operations
193
194
Utilities for updating document fields with conflict resolution.
195
196
```python { .api }
197
class Document(dict):
198
"""Field update operations."""
199
200
def update_field(self, action, field, value, max_tries=10):
201
"""
202
Update document field with conflict resolution.
203
204
Parameters:
205
- action (callable): Update function (list_field_append, list_field_remove, field_set)
206
- field (str): Field name to update
207
- value: Value for the update operation
208
- max_tries (int): Maximum retry attempts for conflicts
209
210
Returns:
211
dict: Update response
212
213
Raises:
214
CloudantDocumentException: Update failed after max retries
215
"""
216
217
@staticmethod
218
def list_field_append(doc, field, value):
219
"""
220
Helper to append value to list field.
221
222
Parameters:
223
- doc (dict): Document data
224
- field (str): Field name
225
- value: Value to append
226
227
Returns:
228
dict: Updated document
229
"""
230
231
@staticmethod
232
def list_field_remove(doc, field, value):
233
"""
234
Helper to remove value from list field.
235
236
Parameters:
237
- doc (dict): Document data
238
- field (str): Field name
239
- value: Value to remove
240
241
Returns:
242
dict: Updated document
243
"""
244
245
@staticmethod
246
def field_set(doc, field, value):
247
"""
248
Helper to set field value.
249
250
Parameters:
251
- doc (dict): Document data
252
- field (str): Field name
253
- value: Value to set
254
255
Returns:
256
dict: Updated document
257
"""
258
```
259
260
### Attachment Operations
261
262
Manage binary attachments on documents.
263
264
```python { .api }
265
class Document(dict):
266
"""Attachment management."""
267
268
def get_attachment(self, attachment, headers=None, write_to=None, attachment_type=None):
269
"""
270
Retrieve document attachment.
271
272
Parameters:
273
- attachment (str): Attachment name
274
- headers (dict): Additional HTTP headers
275
- write_to (file-like): Stream attachment to file object
276
- attachment_type (str): Expected MIME type
277
278
Returns:
279
bytes | None: Attachment content (None if write_to specified)
280
281
Raises:
282
CloudantDocumentException: Attachment not found or retrieval failed
283
"""
284
285
def put_attachment(self, attachment, content_type, data, headers=None):
286
"""
287
Add or update document attachment.
288
289
Parameters:
290
- attachment (str): Attachment name
291
- content_type (str): MIME type of attachment
292
- data (bytes | file-like): Attachment content
293
- headers (dict): Additional HTTP headers
294
295
Returns:
296
dict: Upload response with updated _rev
297
298
Raises:
299
CloudantDocumentException: Upload failed
300
"""
301
302
def delete_attachment(self, attachment, headers=None):
303
"""
304
Delete document attachment.
305
306
Parameters:
307
- attachment (str): Attachment name to delete
308
- headers (dict): Additional HTTP headers
309
310
Returns:
311
dict: Deletion response with updated _rev
312
313
Raises:
314
CloudantDocumentException: Deletion failed
315
"""
316
```
317
318
### Context Manager Support
319
320
Automatic fetching and saving with context manager.
321
322
```python { .api }
323
class Document(dict):
324
"""Context manager support."""
325
326
def __enter__(self):
327
"""
328
Context manager entry - fetch document if it exists.
329
330
Returns:
331
Document: Self reference
332
"""
333
334
def __exit__(self, exc_type, exc_value, traceback):
335
"""
336
Context manager exit - save document if no exception.
337
338
Parameters:
339
- exc_type: Exception type (if any)
340
- exc_value: Exception value (if any)
341
- traceback: Exception traceback (if any)
342
343
Returns:
344
bool: False (don't suppress exceptions)
345
"""
346
```
347
348
### Bulk Operations
349
350
Batch operations for multiple documents.
351
352
```python { .api }
353
class CouchDatabase(dict):
354
"""Bulk document operations."""
355
356
def bulk_docs(self, docs):
357
"""
358
Perform bulk document operations.
359
360
Parameters:
361
- docs (list[dict]): List of documents to create/update/delete
362
Each doc can include _deleted: true for deletion
363
364
Returns:
365
list[dict]: List of operation results with _id, _rev, or error info
366
367
Raises:
368
CloudantDatabaseException: Bulk operation failed
369
"""
370
```
371
372
### Document Querying
373
374
Primary index and document iteration.
375
376
```python { .api }
377
class CouchDatabase(dict):
378
"""Document querying."""
379
380
def all_docs(self, **kwargs):
381
"""
382
Query primary index (_all_docs).
383
384
Parameters:
385
- include_docs (bool): Include document content
386
- startkey (str): Start key for range
387
- endkey (str): End key for range
388
- keys (list[str]): Specific document IDs to retrieve
389
- limit (int): Maximum results
390
- skip (int): Number of results to skip
391
- descending (bool): Reverse order
392
- inclusive_end (bool): Include endkey in results
393
394
Returns:
395
Result: Query result iterator
396
"""
397
398
def partitioned_all_docs(self, partition_key, **kwargs):
399
"""
400
Query primary index for specific partition.
401
402
Parameters:
403
- partition_key (str): Partition identifier
404
- **kwargs: Same options as all_docs()
405
406
Returns:
407
Result: Query result iterator
408
"""
409
```
410
411
## Usage Examples
412
413
### Basic Document Operations
414
415
```python
416
from cloudant import cloudant
417
418
with cloudant('user', 'pass', account='myaccount') as client:
419
db = client['my_database']
420
421
# Create document with specific ID
422
doc_data = {
423
'_id': 'user123',
424
'name': 'John Doe',
425
'email': 'john@example.com',
426
'tags': ['developer', 'python']
427
}
428
doc = db.create_document(doc_data)
429
print(f"Created: {doc['_id']} with rev {doc['_rev']}")
430
431
# Create document with auto-generated ID
432
new_doc = db.new_document()
433
new_doc['name'] = 'Jane Smith'
434
new_doc['role'] = 'designer'
435
new_doc.create()
436
437
# Access existing document
438
user_doc = db['user123']
439
440
# Check if document exists
441
if user_doc.exists():
442
# Fetch latest version
443
user_doc.fetch()
444
print(f"User: {user_doc['name']}")
445
446
# Update document
447
user_doc['last_login'] = '2023-01-15'
448
user_doc['tags'].append('admin')
449
user_doc.save()
450
451
# Delete document
452
user_doc.delete()
453
```
454
455
### Context Manager Usage
456
457
```python
458
from cloudant import cloudant
459
460
with cloudant('user', 'pass', account='myaccount') as client:
461
db = client['my_database']
462
463
# Automatic fetch and save
464
with db['user123'] as doc:
465
doc['last_updated'] = '2023-01-15'
466
doc['login_count'] = doc.get('login_count', 0) + 1
467
# Document is automatically saved on context exit
468
```
469
470
### Field Operations with Conflict Resolution
471
472
```python
473
from cloudant import cloudant
474
from cloudant.document import Document
475
476
with cloudant('user', 'pass', account='myaccount') as client:
477
db = client['my_database']
478
doc = db['user123']
479
480
# Append to list field with automatic conflict resolution
481
doc.update_field(Document.list_field_append, 'tags', 'admin')
482
483
# Remove from list field
484
doc.update_field(Document.list_field_remove, 'tags', 'developer')
485
486
# Set field value
487
doc.update_field(Document.field_set, 'status', 'active')
488
```
489
490
### Attachment Operations
491
492
```python
493
from cloudant import cloudant
494
495
with cloudant('user', 'pass', account='myaccount') as client:
496
db = client['my_database']
497
doc = db['user123']
498
499
# Add attachment
500
with open('profile.jpg', 'rb') as f:
501
attachment_data = f.read()
502
503
doc.put_attachment('profile_picture', 'image/jpeg', attachment_data)
504
505
# Retrieve attachment
506
image_data = doc.get_attachment('profile_picture')
507
508
# Save attachment to file
509
with open('downloaded_profile.jpg', 'wb') as f:
510
doc.get_attachment('profile_picture', write_to=f)
511
512
# Delete attachment
513
doc.delete_attachment('profile_picture')
514
```
515
516
### Bulk Operations
517
518
```python
519
from cloudant import cloudant
520
521
with cloudant('user', 'pass', account='myaccount') as client:
522
db = client['my_database']
523
524
# Bulk create/update documents
525
docs = [
526
{'_id': 'user1', 'name': 'Alice', 'role': 'admin'},
527
{'_id': 'user2', 'name': 'Bob', 'role': 'user'},
528
{'_id': 'user3', 'name': 'Carol', 'role': 'moderator'}
529
]
530
531
results = db.bulk_docs(docs)
532
for result in results:
533
if 'error' in result:
534
print(f"Error for {result['id']}: {result['error']}")
535
else:
536
print(f"Success: {result['id']} -> {result['rev']}")
537
538
# Bulk delete documents
539
delete_docs = [
540
{'_id': 'user1', '_rev': '1-abc123', '_deleted': True},
541
{'_id': 'user2', '_rev': '1-def456', '_deleted': True}
542
]
543
544
db.bulk_docs(delete_docs)
545
```
546
547
### Document Iteration and Querying
548
549
```python
550
from cloudant import cloudant
551
552
with cloudant('user', 'pass', account='myaccount') as client:
553
db = client['my_database']
554
555
# Iterate over all documents
556
for doc in db:
557
print(f"Document: {doc['_id']}")
558
559
# Query with all_docs
560
result = db.all_docs(include_docs=True, limit=10)
561
for row in result:
562
doc = row['doc']
563
print(f"ID: {doc['_id']}, Name: {doc.get('name', 'N/A')}")
564
565
# Query specific documents
566
user_ids = ['user1', 'user2', 'user3']
567
result = db.all_docs(keys=user_ids, include_docs=True)
568
for row in result:
569
if 'doc' in row:
570
print(f"Found user: {row['doc']['name']}")
571
572
# Range query
573
result = db.all_docs(
574
startkey='user',
575
endkey='user\ufff0', # Unicode high character for prefix matching
576
include_docs=True
577
)
578
for row in result:
579
print(f"User document: {row['doc']['_id']}")
580
```
581
582
### Partitioned Document Operations (Cloudant)
583
584
```python
585
from cloudant import cloudant
586
587
with cloudant('user', 'pass', account='myaccount') as client:
588
# Create partitioned database
589
db = client.create_database('partitioned_db', partitioned=True)
590
591
# Create document in partition
592
doc = db.create_document({
593
'_id': 'user123:profile', # partition_key:document_id format
594
'name': 'John Doe',
595
'partition': 'user123'
596
})
597
598
# Query partition
599
result = db.partitioned_all_docs('user123', include_docs=True)
600
for row in result:
601
print(f"Partition doc: {row['doc']['_id']}")
602
```
603
604
## Error Handling
605
606
Document operations can raise `CloudantDocumentException`:
607
608
```python
609
from cloudant import cloudant
610
from cloudant.error import CloudantDocumentException
611
612
with cloudant('user', 'pass', account='myaccount') as client:
613
db = client['my_database']
614
615
try:
616
# Try to fetch non-existent document
617
doc = db['non_existent']
618
doc.fetch()
619
except CloudantDocumentException as e:
620
print(f"Document not found: {e}")
621
622
try:
623
# Try to save document with stale revision
624
doc = db['user123']
625
doc.fetch()
626
doc['_rev'] = '1-old_revision' # Simulate stale revision
627
doc['name'] = 'Updated Name'
628
doc.save()
629
except CloudantDocumentException as e:
630
print(f"Conflict error: {e}")
631
# Fetch latest and retry
632
doc.fetch()
633
doc['name'] = 'Updated Name'
634
doc.save()
635
```