0
# Views and Design Documents
1
2
Manage MapReduce views, list functions, show functions, and update handlers through design documents. Design documents are special documents that contain application code for database operations including views, indexes, and server-side functions.
3
4
## Capabilities
5
6
### Design Document Management
7
8
Access and manage design documents containing views and functions.
9
10
```python { .api }
11
class CouchDatabase(dict):
12
"""Design document access methods."""
13
14
def design_documents(self):
15
"""
16
Get all design documents in the database.
17
18
Returns:
19
dict: All design documents keyed by design document ID
20
"""
21
22
def list_design_documents(self):
23
"""
24
List design document names.
25
26
Returns:
27
list[str]: Design document IDs (without _design/ prefix)
28
"""
29
30
def get_design_document(self, ddoc_id):
31
"""
32
Get specific design document.
33
34
Parameters:
35
- ddoc_id (str): Design document ID (without _design/ prefix)
36
37
Returns:
38
DesignDocument: Design document instance
39
40
Raises:
41
CloudantDesignDocumentException: Design document not found
42
"""
43
```
44
45
### DesignDocument Class
46
47
Special document type for managing views, indexes, and functions.
48
49
```python { .api }
50
class DesignDocument(Document):
51
"""
52
Design document containing views, indexes, and server-side functions.
53
"""
54
55
def __init__(self, database, document_id=None, partitioned=False):
56
"""
57
Initialize design document.
58
59
Parameters:
60
- database (CouchDatabase | CloudantDatabase): Parent database
61
- document_id (str): Design document ID (without _design/ prefix)
62
- partitioned (bool): Whether views are partitioned
63
"""
64
65
@property
66
def views(self):
67
"""
68
MapReduce views in this design document.
69
70
Returns:
71
dict: View definitions with map/reduce functions
72
"""
73
74
@property
75
def shows(self):
76
"""
77
Show functions for formatting documents.
78
79
Returns:
80
dict: Show function definitions
81
"""
82
83
@property
84
def lists(self):
85
"""
86
List functions for formatting view results.
87
88
Returns:
89
dict: List function definitions
90
"""
91
92
@property
93
def updates(self):
94
"""
95
Update handlers for document modifications.
96
97
Returns:
98
dict: Update handler definitions
99
"""
100
101
@property
102
def filters(self):
103
"""
104
Filter functions for change feeds.
105
106
Returns:
107
dict: Filter function definitions
108
"""
109
110
@property
111
def validate_doc_update(self):
112
"""
113
Document validation function.
114
115
Returns:
116
str: Validation function JavaScript code
117
"""
118
119
@property
120
def rewrites(self):
121
"""
122
URL rewrite rules.
123
124
Returns:
125
list[dict]: Rewrite rule definitions
126
"""
127
128
@property
129
def indexes(self):
130
"""
131
Search indexes (Cloudant only).
132
133
Returns:
134
dict: Search index definitions
135
"""
136
```
137
138
### View Class
139
140
Individual MapReduce view management.
141
142
```python { .api }
143
class View:
144
"""
145
MapReduce view within a design document.
146
"""
147
148
def __init__(self, design_document, view_name, **kwargs):
149
"""
150
Initialize view instance.
151
152
Parameters:
153
- design_document (DesignDocument): Parent design document
154
- view_name (str): View name within design document
155
- **kwargs: View options and parameters
156
"""
157
158
def __call__(self, **kwargs):
159
"""
160
Execute view query.
161
162
Parameters:
163
- key: Single key to query
164
- keys (list): Multiple keys to query
165
- startkey: Start of key range
166
- endkey: End of key range
167
- startkey_docid (str): Start document ID for pagination
168
- endkey_docid (str): End document ID for pagination
169
- limit (int): Maximum results
170
- skip (int): Number of results to skip
171
- descending (bool): Reverse sort order
172
- group (bool): Group results by key
173
- group_level (int): Group level for complex keys
174
- reduce (bool): Use reduce function (default: True if available)
175
- include_docs (bool): Include document content
176
- inclusive_end (bool): Include endkey in results
177
- update_seq (bool): Include update sequence in response
178
- conflicts (bool): Include conflict information
179
- attachments (bool): Include attachment stubs
180
- att_encoding_info (bool): Include attachment encoding info
181
182
Returns:
183
Result: View result iterator
184
185
Raises:
186
CloudantViewException: View query failed
187
"""
188
189
def custom_result(self, **options):
190
"""
191
Execute view with custom result options.
192
193
Parameters:
194
- **options: Result collection options
195
196
Returns:
197
Result: Custom result iterator
198
"""
199
200
@property
201
def result(self):
202
"""Default view result collection"""
203
204
@property
205
def map_function(self):
206
"""
207
JavaScript map function code.
208
209
Returns:
210
str: Map function JavaScript code
211
"""
212
213
@property
214
def reduce_function(self):
215
"""
216
JavaScript reduce function code.
217
218
Returns:
219
str | None: Reduce function JavaScript code or None
220
"""
221
```
222
223
### View Execution Methods
224
225
Database methods for executing views.
226
227
```python { .api }
228
class CouchDatabase(dict):
229
"""View execution methods."""
230
231
def get_view_result(self, ddoc_id, view_name, raw_result=False, **kwargs):
232
"""
233
Execute MapReduce view and return results.
234
235
Parameters:
236
- ddoc_id (str): Design document ID (without _design/ prefix)
237
- view_name (str): View name within design document
238
- raw_result (bool): Return raw response without processing
239
- **kwargs: View query parameters (same as View.__call__)
240
241
Returns:
242
Result | dict: View results or raw response
243
244
Raises:
245
CloudantViewException: View execution failed
246
"""
247
248
def get_partitioned_view_result(self, partition_key, ddoc_id, view_name, raw_result=False, **kwargs):
249
"""
250
Execute view within specific partition.
251
252
Parameters:
253
- partition_key (str): Partition identifier
254
- ddoc_id (str): Design document ID
255
- view_name (str): View name
256
- raw_result (bool): Return raw response
257
- **kwargs: View query parameters
258
259
Returns:
260
Result | dict: Partitioned view results
261
"""
262
```
263
264
### List and Show Functions
265
266
Server-side formatting functions.
267
268
```python { .api }
269
class CouchDatabase(dict):
270
"""List and show function execution."""
271
272
def get_list_function_result(self, ddoc_id, list_name, view_name, **kwargs):
273
"""
274
Execute list function to format view results.
275
276
Parameters:
277
- ddoc_id (str): Design document ID
278
- list_name (str): List function name
279
- view_name (str): View name to format
280
- **kwargs: View parameters and list options
281
282
Returns:
283
str: Formatted output from list function
284
285
Raises:
286
CloudantDesignDocumentException: List function execution failed
287
"""
288
289
def get_show_function_result(self, ddoc_id, show_name, doc_id):
290
"""
291
Execute show function to format document.
292
293
Parameters:
294
- ddoc_id (str): Design document ID
295
- show_name (str): Show function name
296
- doc_id (str): Document ID to format
297
298
Returns:
299
str: Formatted output from show function
300
301
Raises:
302
CloudantDesignDocumentException: Show function execution failed
303
"""
304
```
305
306
### Update Handlers
307
308
Server-side document update functions.
309
310
```python { .api }
311
class CouchDatabase(dict):
312
"""Update handler execution."""
313
314
def update_handler_result(self, ddoc_id, handler_name, doc_id=None, data=None, **params):
315
"""
316
Execute update handler function.
317
318
Parameters:
319
- ddoc_id (str): Design document ID
320
- handler_name (str): Update handler name
321
- doc_id (str): Document ID to update (optional for create)
322
- data (dict | str): Request body data
323
- **params: Additional URL parameters
324
325
Returns:
326
dict: Update handler response
327
328
Raises:
329
CloudantDesignDocumentException: Update handler execution failed
330
"""
331
```
332
333
## Usage Examples
334
335
### Creating Design Documents
336
337
```python
338
from cloudant import cloudant
339
from cloudant.design_document import DesignDocument
340
341
with cloudant('user', 'pass', account='myaccount') as client:
342
db = client['my_database']
343
344
# Create design document with views
345
ddoc = DesignDocument(db, 'user_views')
346
347
# Add MapReduce view
348
ddoc['views'] = {
349
'by_status': {
350
'map': '''
351
function(doc) {
352
if (doc.type === 'user') {
353
emit(doc.status, {
354
name: doc.name,
355
email: doc.email,
356
created: doc.created_date
357
});
358
}
359
}
360
'''
361
},
362
'count_by_department': {
363
'map': '''
364
function(doc) {
365
if (doc.type === 'user' && doc.department) {
366
emit(doc.department, 1);
367
}
368
}
369
''',
370
'reduce': '_count' # Built-in reduce function
371
}
372
}
373
374
# Add show function
375
ddoc['shows'] = {
376
'user_profile': '''
377
function(doc, req) {
378
if (!doc) {
379
return {
380
code: 404,
381
body: 'User not found'
382
};
383
}
384
385
var html = '<h1>' + doc.name + '</h1>';
386
html += '<p>Email: ' + doc.email + '</p>';
387
html += '<p>Status: ' + doc.status + '</p>';
388
389
return {
390
headers: {'Content-Type': 'text/html'},
391
body: html
392
};
393
}
394
'''
395
}
396
397
# Add list function
398
ddoc['lists'] = {
399
'user_csv': '''
400
function(head, req) {
401
var row;
402
start({headers: {'Content-Type': 'text/csv'}});
403
send('name,email,status\\n');
404
405
while (row = getRow()) {
406
var user = row.value;
407
send(user.name + ',' + user.email + ',' + row.key + '\\n');
408
}
409
}
410
'''
411
}
412
413
# Add update handler
414
ddoc['updates'] = {
415
'status_update': '''
416
function(doc, req) {
417
if (!doc) {
418
return [null, {
419
code: 404,
420
body: 'Document not found'
421
}];
422
}
423
424
var new_status = req.form.status;
425
if (new_status) {
426
doc.status = new_status;
427
doc.updated_date = new Date().toISOString();
428
429
return [doc, {
430
code: 200,
431
headers: {'Content-Type': 'application/json'},
432
body: JSON.stringify({status: 'updated'})
433
}];
434
}
435
436
return [null, {
437
code: 400,
438
body: 'Status parameter required'
439
}];
440
}
441
'''
442
}
443
444
# Save design document
445
ddoc.save()
446
print(f"Created design document: {ddoc['_id']}")
447
```
448
449
### Querying Views
450
451
```python
452
from cloudant import cloudant
453
454
with cloudant('user', 'pass', account='myaccount') as client:
455
db = client['my_database']
456
457
# Query view with specific key
458
result = db.get_view_result('user_views', 'by_status', key='active')
459
for row in result:
460
user = row['value']
461
print(f"Active user: {user['name']} ({user['email']})")
462
463
# Query view with key range
464
result = db.get_view_result(
465
'user_views',
466
'by_status',
467
startkey='active',
468
endkey='pending',
469
include_docs=True
470
)
471
472
for row in result:
473
print(f"User {row['key']}: {row['doc']['name']}")
474
475
# Query with multiple keys
476
statuses = ['active', 'pending', 'suspended']
477
result = db.get_view_result('user_views', 'by_status', keys=statuses)
478
479
# Reduced view (count)
480
result = db.get_view_result('user_views', 'count_by_department')
481
for row in result:
482
print(f"Department {row['key']}: {row['value']} users")
483
```
484
485
### Using View Objects
486
487
```python
488
from cloudant import cloudant
489
from cloudant.view import View
490
491
with cloudant('user', 'pass', account='myaccount') as client:
492
db = client['my_database']
493
ddoc = db.get_design_document('user_views')
494
495
# Create reusable view object
496
status_view = View(ddoc, 'by_status')
497
498
# Query active users
499
active_users = status_view(key='active', include_docs=True)
500
for row in active_users:
501
print(f"Active: {row['doc']['name']}")
502
503
# Query with custom result options
504
paginated_result = status_view.custom_result(
505
limit=10,
506
skip=0,
507
key='active'
508
)
509
510
for row in paginated_result:
511
print(f"User: {row['value']['name']}")
512
```
513
514
### Complex View Queries
515
516
```python
517
from cloudant import cloudant
518
519
with cloudant('user', 'pass', account='myaccount') as client:
520
db = client['my_database']
521
522
# Pagination with startkey_docid
523
last_key = None
524
last_docid = None
525
page_size = 25
526
527
while True:
528
params = {
529
'limit': page_size + 1, # Get one extra to check for more
530
'include_docs': True
531
}
532
533
if last_key is not None:
534
params['startkey'] = last_key
535
params['startkey_docid'] = last_docid
536
params['skip'] = 1 # Skip the last doc from previous page
537
538
result = db.get_view_result('user_views', 'by_status', **params)
539
rows = list(result)
540
541
if not rows:
542
break
543
544
# Process current page (excluding extra row)
545
page_rows = rows[:page_size]
546
for row in page_rows:
547
print(f"User: {row['doc']['name']}")
548
549
# Check if there are more pages
550
if len(rows) <= page_size:
551
break
552
553
# Set pagination markers for next page
554
last_row = rows[page_size - 1]
555
last_key = last_row['key']
556
last_docid = last_row['id']
557
558
# Group reduce queries
559
result = db.get_view_result(
560
'user_views',
561
'count_by_department',
562
group=True,
563
group_level=1
564
)
565
566
for row in result:
567
print(f"Department: {row['key']}, Count: {row['value']}")
568
```
569
570
### List and Show Functions
571
572
```python
573
from cloudant import cloudant
574
575
with cloudant('user', 'pass', account='myaccount') as client:
576
db = client['my_database']
577
578
# Execute show function
579
html_output = db.get_show_function_result(
580
'user_views',
581
'user_profile',
582
'user123'
583
)
584
print("HTML Profile:", html_output)
585
586
# Execute list function
587
csv_output = db.get_list_function_result(
588
'user_views',
589
'user_csv',
590
'by_status',
591
key='active'
592
)
593
print("CSV Export:", csv_output)
594
```
595
596
### Update Handlers
597
598
```python
599
from cloudant import cloudant
600
import json
601
602
with cloudant('user', 'pass', account='myaccount') as client:
603
db = client['my_database']
604
605
# Execute update handler
606
response = db.update_handler_result(
607
'user_views',
608
'status_update',
609
'user123',
610
data={'status': 'suspended'}
611
)
612
613
print(f"Update response: {response}")
614
615
# Update handler for new document
616
response = db.update_handler_result(
617
'user_views',
618
'create_user',
619
data=json.dumps({
620
'name': 'New User',
621
'email': 'newuser@example.com',
622
'status': 'pending'
623
})
624
)
625
```
626
627
### Partitioned Views (Cloudant)
628
629
```python
630
from cloudant import cloudant
631
632
with cloudant('user', 'pass', account='myaccount') as client:
633
# Use partitioned database
634
db = client['partitioned_database']
635
636
# Create partitioned design document
637
ddoc = db.get_design_document('partition_views')
638
ddoc['views'] = {
639
'by_activity_type': {
640
'map': '''
641
function(doc) {
642
if (doc.type === 'activity') {
643
emit(doc.activity_type, {
644
timestamp: doc.timestamp,
645
details: doc.details
646
});
647
}
648
}
649
'''
650
}
651
}
652
ddoc['options'] = {'partitioned': True}
653
ddoc.save()
654
655
# Query view within partition
656
result = db.get_partitioned_view_result(
657
'user123', # partition key
658
'partition_views',
659
'by_activity_type',
660
key='login',
661
limit=10
662
)
663
664
for row in result:
665
activity = row['value']
666
print(f"Login activity: {activity['timestamp']}")
667
```
668
669
### View Performance Optimization
670
671
```python
672
from cloudant import cloudant
673
674
with cloudant('user', 'pass', account='myaccount') as client:
675
db = client['my_database']
676
677
# Query view without reduce for better performance on large datasets
678
result = db.get_view_result(
679
'user_views',
680
'count_by_department',
681
reduce=False, # Skip reduce function
682
include_docs=False # Don't fetch full documents
683
)
684
685
# Count manually if needed
686
department_counts = {}
687
for row in result:
688
dept = row['key']
689
department_counts[dept] = department_counts.get(dept, 0) + 1
690
691
# Use stale views for better performance (eventual consistency)
692
result = db.get_view_result(
693
'user_views',
694
'by_status',
695
stale='ok', # Don't wait for index updates
696
key='active'
697
)
698
```
699
700
## Error Handling
701
702
View and design document operations can raise specific exceptions:
703
704
```python
705
from cloudant import cloudant
706
from cloudant.error import (
707
CloudantDesignDocumentException,
708
CloudantViewException
709
)
710
711
with cloudant('user', 'pass', account='myaccount') as client:
712
db = client['my_database']
713
714
try:
715
# Query non-existent view
716
result = db.get_view_result('missing_ddoc', 'missing_view')
717
except CloudantViewException as e:
718
print(f"View query failed: {e}")
719
720
try:
721
# Execute non-existent show function
722
output = db.get_show_function_result('ddoc', 'missing_show', 'doc123')
723
except CloudantDesignDocumentException as e:
724
print(f"Show function failed: {e}")
725
726
try:
727
# Create design document with invalid JavaScript
728
ddoc = db.get_design_document('invalid_views')
729
ddoc['views'] = {
730
'bad_view': {
731
'map': 'invalid javascript code here'
732
}
733
}
734
ddoc.save()
735
except CloudantDesignDocumentException as e:
736
print(f"Design document save failed: {e}")
737
```