0
# Query and Indexing
1
2
Query documents using Cloudant Query (Mango) with comprehensive indexing support including JSON indexes, text search, and special indexes with pagination and partitioning capabilities.
3
4
## Capabilities
5
6
### Query Class
7
8
Execute queries using Cloudant Query (Mango) syntax.
9
10
```python { .api }
11
class Query:
12
"""
13
Cloudant query interface using Mango query language.
14
"""
15
16
def __init__(self, database, selector=None, fields=None, **kwargs):
17
"""
18
Initialize query builder.
19
20
Parameters:
21
- database (CloudantDatabase): Target database
22
- selector (dict): Query selector (WHERE clause)
23
- fields (list[str]): Fields to return (SELECT clause)
24
- sort (list[dict]): Sort specification
25
- limit (int): Maximum results
26
- skip (int): Number of results to skip
27
- bookmark (str): Pagination bookmark
28
- use_index (str | list): Index to use for query
29
- execution_stats (bool): Include execution statistics
30
"""
31
32
def __call__(self, **kwargs):
33
"""
34
Execute query with optional parameter overrides.
35
36
Parameters:
37
- **kwargs: Query parameters to override
38
39
Returns:
40
QueryResult: Query result iterator with bookmark support
41
"""
42
43
def custom_result(self, **options):
44
"""
45
Execute query with custom result options.
46
47
Parameters:
48
- **options: Result collection options
49
50
Returns:
51
QueryResult: Custom result iterator
52
"""
53
54
@property
55
def result(self):
56
"""Default query result collection"""
57
```
58
59
### Database Query Methods
60
61
Direct query execution methods on database instances.
62
63
```python { .api }
64
class CouchDatabase(dict):
65
"""Database query methods."""
66
67
def get_query_result(self, selector, fields=None, raw_result=False, **kwargs):
68
"""
69
Execute Cloudant Query and return results.
70
71
Parameters:
72
- selector (dict): Query selector using MongoDB-style syntax
73
- fields (list[str]): Fields to return in results
74
- raw_result (bool): Return raw response without processing
75
- sort (list[dict]): Sort specification [{"field": "asc|desc"}]
76
- limit (int): Maximum number of results
77
- skip (int): Number of results to skip
78
- bookmark (str): Pagination bookmark from previous query
79
- use_index (str | list): Force use of specific index
80
- execution_stats (bool): Include query execution statistics
81
82
Returns:
83
QueryResult | dict: Query results or raw response
84
85
Raises:
86
CloudantDatabaseException: Query execution failed
87
"""
88
89
def get_partitioned_query_result(self, partition_key, selector, fields=None, raw_result=False, **kwargs):
90
"""
91
Execute query within a specific partition.
92
93
Parameters:
94
- partition_key (str): Partition identifier
95
- selector (dict): Query selector
96
- fields (list[str]): Fields to return
97
- raw_result (bool): Return raw response
98
- **kwargs: Same options as get_query_result()
99
100
Returns:
101
QueryResult | dict: Partitioned query results
102
"""
103
```
104
105
### Index Management
106
107
Create and manage indexes for query optimization.
108
109
```python { .api }
110
class CouchDatabase(dict):
111
"""Index management methods."""
112
113
def get_query_indexes(self, raw_result=False):
114
"""
115
List all query indexes in the database.
116
117
Parameters:
118
- raw_result (bool): Return raw response
119
120
Returns:
121
list[dict] | dict: Index definitions or raw response
122
"""
123
124
def create_query_index(self, design_document_id=None, index_name=None, index_type='json', partitioned=None, **kwargs):
125
"""
126
Create a query index.
127
128
Parameters:
129
- design_document_id (str): Design document ID for index
130
- index_name (str): Name for the index
131
- index_type (str): Index type ('json', 'text', 'special')
132
- partitioned (bool): Whether index is partitioned
133
- fields (list[str | dict]): Fields to index
134
- selector (dict): Partial filter selector for index
135
136
Returns:
137
dict: Index creation response
138
139
Raises:
140
CloudantIndexException: Index creation failed
141
"""
142
143
def delete_query_index(self, design_document_id, index_type, index_name):
144
"""
145
Delete a query index.
146
147
Parameters:
148
- design_document_id (str): Design document containing index
149
- index_type (str): Type of index to delete
150
- index_name (str): Name of index to delete
151
152
Returns:
153
dict: Deletion response
154
155
Raises:
156
CloudantIndexException: Index deletion failed
157
"""
158
```
159
160
### Index Classes
161
162
Object-oriented index management.
163
164
```python { .api }
165
class Index:
166
"""
167
JSON query index management.
168
"""
169
170
def __init__(self, database, design_document_id=None, name=None, partitioned=None, **kwargs):
171
"""
172
Initialize index instance.
173
174
Parameters:
175
- database (CloudantDatabase): Target database
176
- design_document_id (str): Design document ID
177
- name (str): Index name
178
- partitioned (bool): Partitioned index flag
179
- fields (list[str | dict]): Fields to index
180
- selector (dict): Partial filter selector
181
"""
182
183
def create(self):
184
"""
185
Create index in database.
186
187
Returns:
188
dict: Creation response
189
190
Raises:
191
CloudantIndexException: Creation failed
192
"""
193
194
def delete(self):
195
"""
196
Delete index from database.
197
198
Returns:
199
dict: Deletion response
200
201
Raises:
202
CloudantIndexException: Deletion failed
203
"""
204
205
@property
206
def index_url(self):
207
"""Index URL endpoint"""
208
209
@property
210
def design_document_id(self):
211
"""Design document ID containing index"""
212
213
@property
214
def name(self):
215
"""Index name"""
216
217
@property
218
def type(self):
219
"""Index type ('json', 'text', 'special')"""
220
221
class TextIndex(Index):
222
"""
223
Text search index for full-text search capabilities.
224
"""
225
226
class SpecialIndex(Index):
227
"""
228
Special index for built-in indexes like _all_docs.
229
"""
230
```
231
232
### Result Collections
233
234
Sliceable, key-accessible, and iterable result collections for efficient data access and pagination.
235
236
```python { .api }
237
class Result:
238
"""
239
Key accessible, sliceable, and iterable interface to result collections.
240
241
Supports efficient paged iteration and flexible data access patterns
242
including index-based access, key-based access, and slicing.
243
"""
244
245
def __init__(self, method_ref, **options):
246
"""
247
Initialize result collection.
248
249
Parameters:
250
- method_ref: Callable that returns JSON result data
251
- descending (bool): Return documents in descending key order
252
- endkey: Stop returning records at specified key
253
- endkey_docid (str): Stop at specified document ID
254
- group (bool): Group reduce results
255
- group_level (int): Group level for complex keys
256
- include_docs (bool): Include full document content
257
- inclusive_end (bool): Include rows with endkey
258
- key: Return only documents matching specified key
259
- keys (list): Return only documents matching specified keys
260
- limit (int): Maximum number of results
261
- page_size (int): Page size for iteration (default: 100)
262
- reduce (bool): Use reduce function
263
- skip (int): Skip specified number of rows
264
- stable (bool): Use stable set of shards
265
- stale (str): Allow stale results ('ok' or 'update_after')
266
- startkey: Start returning records from specified key
267
- startkey_docid (str): Start from specified document ID
268
- update (str): Update view before/after response ('true', 'false', 'lazy')
269
"""
270
271
def __getitem__(self, arg):
272
"""
273
Key access and slicing support.
274
275
Parameters:
276
- arg (int): Index access - skip N records, get next
277
- arg (str | list | ResultByKey): Key access - get records matching key
278
- arg (slice): Slice access - get range of records
279
280
Returns:
281
list: Rows data in JSON format
282
"""
283
284
def __iter__(self):
285
"""
286
Iterate over result collection with automatic pagination.
287
288
Yields:
289
dict: Individual result documents
290
"""
291
292
def all(self):
293
"""
294
Retrieve all results as a list.
295
296
Returns:
297
list: All result documents
298
"""
299
300
class ResultByKey:
301
"""
302
Wrapper for values used to retrieve records by document key.
303
304
Distinguishes between index access and key access when key is numeric.
305
"""
306
307
def __init__(self, value):
308
"""
309
Initialize key wrapper.
310
311
Parameters:
312
- value: The key value to match against
313
"""
314
315
def __call__(self):
316
"""
317
Get the wrapped key value.
318
319
Returns:
320
The original key value
321
"""
322
323
class QueryResult(Result):
324
"""
325
Specialized result collection for Cloudant Query results.
326
327
Extends Result with query-specific iteration using bookmarks
328
and index-only access patterns.
329
"""
330
331
def __init__(self, query, **options):
332
"""
333
Initialize query result collection.
334
335
Parameters:
336
- query: Query callable reference
337
- bookmark (str): Pagination bookmark
338
- fields (list[str]): Fields to return
339
- page_size (int): Page size for iteration (default: 100)
340
- r (int): Read quorum for results
341
- selector (dict): Query selector criteria
342
- sort (list): Sort specification
343
- use_index (str): Specific index to use
344
"""
345
346
def __getitem__(self, arg):
347
"""
348
Index-only access and slicing for query results.
349
350
Parameters:
351
- arg (int): Index access only
352
- arg (slice): Index slice only (no key-based slicing)
353
354
Returns:
355
list: Document data in JSON format
356
357
Raises:
358
ResultException: If key-based access attempted
359
"""
360
```
361
362
### Search Capabilities (Cloudant)
363
364
Full-text search using Lucene indexes.
365
366
```python { .api }
367
class CloudantDatabase(CouchDatabase):
368
"""Search methods."""
369
370
def get_search_result(self, ddoc_id, index_name, **query_params):
371
"""
372
Execute full-text search query.
373
374
Parameters:
375
- ddoc_id (str): Design document ID containing search index
376
- index_name (str): Search index name
377
- q (str): Lucene query string
378
- bookmark (str): Pagination bookmark
379
- limit (int): Maximum results
380
- sort (str | list): Sort specification
381
- include_docs (bool): Include document content
382
- include_fields (list[str]): Specific fields to include
383
- ranges (dict): Numeric range facets
384
- counts (list[str]): String facet counts
385
- drilldown (list[list]): Facet drill-down
386
- group_field (str): Group results by field
387
- group_limit (int): Results per group
388
- group_sort (str | list): Sort within groups
389
390
Returns:
391
dict: Search results with hits, facets, and metadata
392
393
Raises:
394
CloudantDatabaseException: Search failed
395
"""
396
397
def get_partitioned_search_result(self, partition_key, ddoc_id, index_name, **query_params):
398
"""
399
Execute search within specific partition.
400
401
Parameters:
402
- partition_key (str): Partition identifier
403
- ddoc_id (str): Design document ID
404
- index_name (str): Search index name
405
- **query_params: Same options as get_search_result()
406
407
Returns:
408
dict: Partitioned search results
409
"""
410
```
411
412
## Usage Examples
413
414
### Basic Queries
415
416
```python
417
from cloudant import cloudant
418
419
with cloudant('user', 'pass', account='myaccount') as client:
420
db = client['my_database']
421
422
# Simple equality query
423
selector = {'type': 'user', 'status': 'active'}
424
result = db.get_query_result(selector)
425
426
for doc in result:
427
print(f"Active user: {doc['name']}")
428
429
# Query with specific fields
430
fields = ['_id', 'name', 'email']
431
result = db.get_query_result(selector, fields=fields)
432
433
for doc in result:
434
print(f"User: {doc['name']} ({doc['email']})")
435
```
436
437
### Complex Queries
438
439
```python
440
from cloudant import cloudant
441
442
with cloudant('user', 'pass', account='myaccount') as client:
443
db = client['my_database']
444
445
# Range and comparison operators
446
selector = {
447
'type': 'order',
448
'total': {'$gt': 100, '$lt': 1000},
449
'status': {'$in': ['pending', 'processing']},
450
'created_date': {'$gte': '2023-01-01'}
451
}
452
453
# Sort by total descending, then by date ascending
454
sort = [{'total': 'desc'}, {'created_date': 'asc'}]
455
456
result = db.get_query_result(
457
selector=selector,
458
sort=sort,
459
limit=50,
460
fields=['_id', 'total', 'status', 'created_date']
461
)
462
463
for doc in result:
464
print(f"Order {doc['_id']}: ${doc['total']} - {doc['status']}")
465
466
# Text search with regex
467
selector = {
468
'name': {'$regex': '(?i)john.*'}, # Case-insensitive regex
469
'tags': {'$all': ['developer', 'python']}
470
}
471
472
result = db.get_query_result(selector)
473
```
474
475
### Query Object Usage
476
477
```python
478
from cloudant import cloudant
479
from cloudant.query import Query
480
481
with cloudant('user', 'pass', account='myaccount') as client:
482
db = client['my_database']
483
484
# Create reusable query
485
user_query = Query(
486
db,
487
selector={'type': 'user'},
488
fields=['_id', 'name', 'email', 'created_date'],
489
sort=[{'created_date': 'desc'}]
490
)
491
492
# Execute with default parameters
493
recent_users = user_query()
494
for user in recent_users:
495
print(f"User: {user['name']}")
496
497
# Execute with parameter overrides
498
active_users = user_query(
499
selector={'type': 'user', 'status': 'active'},
500
limit=10
501
)
502
503
for user in active_users:
504
print(f"Active user: {user['name']}")
505
```
506
507
### Index Management
508
509
```python
510
from cloudant import cloudant
511
from cloudant.index import Index, TextIndex
512
513
with cloudant('user', 'pass', account='myaccount') as client:
514
db = client['my_database']
515
516
# List existing indexes
517
indexes = db.get_query_indexes()
518
for idx in indexes['indexes']:
519
print(f"Index: {idx['name']} on {idx['def']['fields']}")
520
521
# Create JSON index
522
db.create_query_index(
523
fields=['type', 'status', 'created_date'],
524
index_name='type_status_date_idx'
525
)
526
527
# Create partial index with selector
528
db.create_query_index(
529
fields=['email'],
530
index_name='user_email_idx',
531
selector={'type': 'user'} # Only index user documents
532
)
533
534
# Using Index class
535
user_index = Index(
536
db,
537
name='user_search_idx',
538
fields=['name', 'email', {'created_date': 'desc'}]
539
)
540
user_index.create()
541
542
# Create text search index
543
search_index = TextIndex(
544
db,
545
name='content_search',
546
fields=[
547
{'name': 'title', 'type': 'string'},
548
{'name': 'content', 'type': 'string'},
549
{'name': 'tags', 'type': 'string'}
550
]
551
)
552
search_index.create()
553
554
# Delete index
555
db.delete_query_index('_design/user_search_idx', 'json', 'user_search_idx')
556
```
557
558
### Pagination
559
560
```python
561
from cloudant import cloudant
562
563
with cloudant('user', 'pass', account='myaccount') as client:
564
db = client['my_database']
565
566
selector = {'type': 'product'}
567
limit = 25
568
bookmark = None
569
page = 1
570
571
while True:
572
result = db.get_query_result(
573
selector=selector,
574
limit=limit,
575
bookmark=bookmark,
576
sort=[{'name': 'asc'}]
577
)
578
579
docs = list(result)
580
if not docs:
581
break
582
583
print(f"Page {page}: {len(docs)} products")
584
for doc in docs:
585
print(f" - {doc['name']}")
586
587
# Get bookmark for next page
588
if hasattr(result, 'bookmark'):
589
bookmark = result.bookmark
590
if not bookmark: # No more pages
591
break
592
else:
593
break
594
595
page += 1
596
```
597
598
### Text Search (Cloudant)
599
600
```python
601
from cloudant import cloudant
602
603
with cloudant('user', 'pass', account='myaccount') as client:
604
db = client['my_database']
605
606
# Simple text search
607
search_result = db.get_search_result(
608
'content_ddoc',
609
'content_search',
610
q='python AND database',
611
limit=20,
612
include_docs=True
613
)
614
615
print(f"Found {search_result['total_rows']} matches")
616
for hit in search_result['rows']:
617
doc = hit['doc']
618
print(f"Match: {doc['title']} (score: {hit['order'][0]})")
619
620
# Advanced search with facets
621
search_result = db.get_search_result(
622
'content_ddoc',
623
'content_search',
624
q='*:*', # Match all documents
625
counts=['category', 'author'], # Facet counts
626
ranges={'price': {'low': '[0 TO 50]', 'high': '[50 TO *]'}},
627
drilldown=[['category', 'electronics']], # Filter by category
628
group_field='author',
629
group_limit=3
630
)
631
632
# Display facets
633
for facet_name, counts in search_result.get('counts', {}).items():
634
print(f"{facet_name} facets:")
635
for value, count in counts.items():
636
print(f" {value}: {count}")
637
```
638
639
### Partitioned Queries (Cloudant)
640
641
```python
642
from cloudant import cloudant
643
644
with cloudant('user', 'pass', account='myaccount') as client:
645
# Use partitioned database
646
db = client['partitioned_database']
647
648
# Query within specific partition
649
result = db.get_partitioned_query_result(
650
'user123',
651
selector={'type': 'activity'},
652
sort=[{'timestamp': 'desc'}],
653
limit=10
654
)
655
656
for activity in result:
657
print(f"Activity: {activity['action']} at {activity['timestamp']}")
658
659
# Search within partition
660
search_result = db.get_partitioned_search_result(
661
'user123',
662
'activity_ddoc',
663
'activity_search',
664
q='login OR logout',
665
include_docs=True
666
)
667
```
668
669
### Result Collections Usage
670
671
```python
672
from cloudant import cloudant
673
from cloudant.result import Result, ResultByKey
674
675
with cloudant('user', 'pass', account='myaccount') as client:
676
db = client['my_database']
677
678
# Create result collection from view
679
result = Result(db.all_docs, include_docs=True)
680
681
# Index-based access
682
first_doc = result[0] # Skip 0, get 1st document
683
tenth_doc = result[9] # Skip 9, get 10th document
684
685
# Key-based access
686
specific_doc = result['doc_key_123'] # Get documents with key 'doc_key_123'
687
numeric_key = result[ResultByKey(42)] # Get documents with numeric key 42
688
689
# Slicing
690
first_ten = result[0:10] # First 10 documents
691
next_batch = result[10:20] # Documents 11-20
692
all_after_100 = result[100:] # All documents after 100th
693
key_range = result['aaa':'zzz'] # Documents with keys between 'aaa' and 'zzz'
694
695
# Iteration with automatic pagination
696
for doc in result:
697
print(f"Document: {doc['_id']}")
698
if doc.get('_id') == 'stop_here':
699
break
700
701
# Get all results at once
702
all_docs = result.all()
703
print(f"Total documents: {len(all_docs)}")
704
705
# Custom page size for iteration
706
large_result = Result(db.all_docs, page_size=1000, include_docs=True)
707
for doc in large_result:
708
process_document(doc)
709
```
710
711
### Query Result Collections
712
713
```python
714
from cloudant import cloudant
715
from cloudant.query import Query
716
from cloudant.result import QueryResult
717
718
with cloudant('user', 'pass', account='myaccount') as client:
719
db = client['my_database']
720
721
# Create query result collection
722
query = Query(db, selector={'type': 'user'})
723
query_result = QueryResult(query, page_size=50)
724
725
# Index-based access only (no key-based access for queries)
726
first_user = query_result[0]
727
users_10_to_20 = query_result[10:20]
728
729
# Iteration with bookmark pagination
730
for user in query_result:
731
print(f"User: {user['name']}")
732
733
# Custom query result with sort
734
sorted_result = QueryResult(
735
query,
736
sort=[{'created_date': 'desc'}],
737
fields=['_id', 'name', 'email']
738
)
739
740
recent_users = sorted_result[0:10] # 10 most recent users
741
```
742
743
### Advanced Result Patterns
744
745
```python
746
from cloudant import cloudant
747
from cloudant.result import Result, ResultByKey
748
749
with cloudant('user', 'pass', account='myaccount') as client:
750
db = client['my_database']
751
752
# Complex key access (for views with complex keys)
753
view_result = Result(db['_design/stats']['by_date_and_type'])
754
755
# Access by complex key
756
today_users = view_result[['2023-12-01', 'user']]
757
date_range = view_result[['2023-12-01']:['2023-12-31']]
758
759
# Numeric key handling
760
numeric_result = Result(db['_design/counters']['by_id'])
761
doc_by_id = numeric_result[ResultByKey(12345)] # Key access, not index
762
twelfth_doc = numeric_result[11] # Index access (12th document)
763
764
# Conditional iteration
765
filtered_result = Result(
766
db.all_docs,
767
startkey='prefix_',
768
endkey='prefix_\ufff0', # All keys starting with 'prefix_'
769
include_docs=True
770
)
771
772
for doc in filtered_result:
773
if doc['doc']['status'] == 'active':
774
print(f"Active document: {doc['id']}")
775
776
# Reduce view results
777
stats_result = Result(
778
db['_design/analytics']['monthly_stats'],
779
group=True,
780
group_level=2
781
)
782
783
for stat in stats_result:
784
month = stat['key']
785
value = stat['value']
786
print(f"Month {month}: {value} events")
787
```
788
789
### Query Performance Optimization
790
791
```python
792
from cloudant import cloudant
793
794
with cloudant('user', 'pass', account='myaccount') as client:
795
db = client['my_database']
796
797
# Force use of specific index
798
result = db.get_query_result(
799
selector={'type': 'user', 'status': 'active'},
800
use_index='user_status_idx',
801
execution_stats=True
802
)
803
804
# Check execution statistics
805
if hasattr(result, 'execution_stats'):
806
stats = result.execution_stats
807
print(f"Execution time: {stats.get('execution_time_ms')}ms")
808
print(f"Results examined: {stats.get('results_returned')}")
809
print(f"Documents examined: {stats.get('total_docs_examined')}")
810
print(f"Index used: {stats.get('index', {}).get('name')}")
811
812
# Query with hint for index selection
813
result = db.get_query_result(
814
selector={'category': 'electronics', 'price': {'$lt': 500}},
815
use_index=['category', 'price'], # Compound index
816
sort=[{'price': 'asc'}]
817
)
818
```
819
820
## Error Handling
821
822
Query and indexing operations can raise specific exceptions:
823
824
```python
825
from cloudant import cloudant
826
from cloudant.error import CloudantDatabaseException, CloudantIndexException
827
828
with cloudant('user', 'pass', account='myaccount') as client:
829
db = client['my_database']
830
831
try:
832
# Query with invalid selector
833
result = db.get_query_result({'invalid_operator': {'$invalid': 'value'}})
834
except CloudantDatabaseException as e:
835
print(f"Query failed: {e}")
836
837
try:
838
# Create duplicate index
839
db.create_query_index(fields=['email'], index_name='existing_index')
840
except CloudantIndexException as e:
841
print(f"Index creation failed: {e}")
842
843
try:
844
# Delete non-existent index
845
db.delete_query_index('_design/missing', 'json', 'missing_index')
846
except CloudantIndexException as e:
847
print(f"Index deletion failed: {e}")
848
```