0
# Subdocument Operations
1
2
Efficient operations on specific paths within JSON documents without retrieving or replacing entire documents. Enables atomic mutations and lookups on document fragments for improved performance and reduced network overhead.
3
4
## Capabilities
5
6
### Document Path Operations
7
8
Perform operations on specific JSON paths within documents.
9
10
```python { .api }
11
class CBCollection:
12
def lookup_in(self, key: str, spec: List[Spec], options: LookupInOptions = None) -> LookupInResult:
13
"""
14
Perform subdocument lookup operations.
15
16
Args:
17
key (str): Document key
18
spec (List[Spec]): List of lookup specifications
19
options (LookupInOptions, optional): Lookup options
20
21
Returns:
22
LookupInResult: Results for each lookup operation
23
24
Raises:
25
DocumentNotFoundException: If document doesn't exist
26
PathNotFoundException: If specified path doesn't exist
27
"""
28
29
def mutate_in(self, key: str, spec: List[Spec], options: MutateInOptions = None) -> MutateInResult:
30
"""
31
Perform subdocument mutation operations.
32
33
Args:
34
key (str): Document key
35
spec (List[Spec]): List of mutation specifications
36
options (MutateInOptions, optional): Mutation options
37
38
Returns:
39
MutateInResult: Results for each mutation operation
40
41
Raises:
42
DocumentNotFoundException: If document doesn't exist
43
PathExistsException: If path already exists (for insert operations)
44
PathNotFoundException: If path doesn't exist (for replace operations)
45
"""
46
```
47
48
### Lookup Specifications
49
50
Specify paths and operations for document lookups.
51
52
```python { .api }
53
import couchbase.subdocument as SD
54
55
class Spec:
56
@staticmethod
57
def get(path: str, xattr: bool = False) -> Spec:
58
"""
59
Get value at path.
60
61
Args:
62
path (str): JSON path
63
xattr (bool): Whether path refers to extended attribute
64
65
Returns:
66
Spec: Lookup specification
67
"""
68
69
@staticmethod
70
def exists(path: str, xattr: bool = False) -> Spec:
71
"""
72
Check if path exists.
73
74
Args:
75
path (str): JSON path
76
xattr (bool): Whether path refers to extended attribute
77
78
Returns:
79
Spec: Existence check specification
80
"""
81
82
@staticmethod
83
def count(path: str, xattr: bool = False) -> Spec:
84
"""
85
Count elements in array at path.
86
87
Args:
88
path (str): JSON path to array
89
xattr (bool): Whether path refers to extended attribute
90
91
Returns:
92
Spec: Count specification
93
"""
94
95
@staticmethod
96
def get_full() -> Spec:
97
"""
98
Get entire document.
99
100
Returns:
101
Spec: Full document retrieval specification
102
"""
103
```
104
105
### Mutation Specifications
106
107
Specify paths and operations for document mutations.
108
109
```python { .api }
110
class Spec:
111
@staticmethod
112
def replace(path: str, value: Any, xattr: bool = False) -> Spec:
113
"""
114
Replace value at path.
115
116
Args:
117
path (str): JSON path
118
value (Any): New value
119
xattr (bool): Whether path refers to extended attribute
120
121
Returns:
122
Spec: Replace specification
123
"""
124
125
@staticmethod
126
def upsert(path: str, value: Any, xattr: bool = False, create_path: bool = False) -> Spec:
127
"""
128
Insert or replace value at path.
129
130
Args:
131
path (str): JSON path
132
value (Any): Value to set
133
xattr (bool): Whether path refers to extended attribute
134
create_path (bool): Create intermediate paths if needed
135
136
Returns:
137
Spec: Upsert specification
138
"""
139
140
@staticmethod
141
def insert(path: str, value: Any, xattr: bool = False, create_path: bool = False) -> Spec:
142
"""
143
Insert value at path (must not exist).
144
145
Args:
146
path (str): JSON path
147
value (Any): Value to insert
148
xattr (bool): Whether path refers to extended attribute
149
create_path (bool): Create intermediate paths if needed
150
151
Returns:
152
Spec: Insert specification
153
"""
154
155
@staticmethod
156
def remove(path: str, xattr: bool = False) -> Spec:
157
"""
158
Remove value at path.
159
160
Args:
161
path (str): JSON path
162
xattr (bool): Whether path refers to extended attribute
163
164
Returns:
165
Spec: Remove specification
166
"""
167
```
168
169
### Array Operations
170
171
Specialized operations for working with JSON arrays.
172
173
```python { .api }
174
class Spec:
175
@staticmethod
176
def array_append(path: str, *values: Any, xattr: bool = False, create_path: bool = False) -> Spec:
177
"""
178
Append values to end of array.
179
180
Args:
181
path (str): JSON path to array
182
*values: Values to append
183
xattr (bool): Whether path refers to extended attribute
184
create_path (bool): Create array if path doesn't exist
185
186
Returns:
187
Spec: Array append specification
188
"""
189
190
@staticmethod
191
def array_prepend(path: str, *values: Any, xattr: bool = False, create_path: bool = False) -> Spec:
192
"""
193
Prepend values to beginning of array.
194
195
Args:
196
path (str): JSON path to array
197
*values: Values to prepend
198
xattr (bool): Whether path refers to extended attribute
199
create_path (bool): Create array if path doesn't exist
200
201
Returns:
202
Spec: Array prepend specification
203
"""
204
205
@staticmethod
206
def array_insert(path: str, *values: Any, xattr: bool = False) -> Spec:
207
"""
208
Insert values at specific array index.
209
210
Args:
211
path (str): JSON path with array index (e.g., "items[2]")
212
*values: Values to insert
213
xattr (bool): Whether path refers to extended attribute
214
215
Returns:
216
Spec: Array insert specification
217
"""
218
219
@staticmethod
220
def array_add_unique(path: str, value: Any, xattr: bool = False, create_path: bool = False) -> Spec:
221
"""
222
Add value to array if not already present.
223
224
Args:
225
path (str): JSON path to array
226
value (Any): Value to add
227
xattr (bool): Whether path refers to extended attribute
228
create_path (bool): Create array if path doesn't exist
229
230
Returns:
231
Spec: Array add unique specification
232
"""
233
```
234
235
### Counter Operations
236
237
Atomic counter operations on numeric values within documents.
238
239
```python { .api }
240
class Spec:
241
@staticmethod
242
def increment(path: str, delta: int = 1, xattr: bool = False, create_path: bool = False) -> Spec:
243
"""
244
Increment numeric value at path.
245
246
Args:
247
path (str): JSON path to numeric value
248
delta (int): Increment amount (default: 1)
249
xattr (bool): Whether path refers to extended attribute
250
create_path (bool): Create path with initial value if needed
251
252
Returns:
253
Spec: Increment specification
254
"""
255
256
@staticmethod
257
def decrement(path: str, delta: int = 1, xattr: bool = False, create_path: bool = False) -> Spec:
258
"""
259
Decrement numeric value at path.
260
261
Args:
262
path (str): JSON path to numeric value
263
delta (int): Decrement amount (default: 1)
264
xattr (bool): Whether path refers to extended attribute
265
create_path (bool): Create path with initial value if needed
266
267
Returns:
268
Spec: Decrement specification
269
"""
270
```
271
272
## Operation Options
273
274
```python { .api }
275
class LookupInOptions:
276
def __init__(self, timeout: timedelta = None,
277
access_deleted: bool = False):
278
"""
279
Options for subdocument lookup operations.
280
281
Args:
282
timeout (timedelta, optional): Operation timeout
283
access_deleted (bool): Access tombstoned (deleted) documents
284
"""
285
286
class MutateInOptions:
287
def __init__(self, timeout: timedelta = None,
288
expiry: timedelta = None,
289
durability: Durability = None,
290
cas: int = None,
291
upsert_document: bool = False,
292
access_deleted: bool = False):
293
"""
294
Options for subdocument mutation operations.
295
296
Args:
297
timeout (timedelta, optional): Operation timeout
298
expiry (timedelta, optional): Document expiration
299
durability (Durability, optional): Durability requirements
300
cas (int, optional): CAS value for optimistic locking
301
upsert_document (bool): Create document if it doesn't exist
302
access_deleted (bool): Access tombstoned (deleted) documents
303
"""
304
```
305
306
## Result Types
307
308
```python { .api }
309
class LookupInResult:
310
def content_as(self, index: int, target_type: type):
311
"""
312
Get content of lookup operation at index.
313
314
Args:
315
index (int): Operation index
316
target_type (type): Target type for content
317
318
Returns:
319
Content converted to target type
320
"""
321
322
def exists(self, index: int) -> bool:
323
"""Check if path exists for operation at index."""
324
325
@property
326
def cas(self) -> int:
327
"""Document CAS value."""
328
329
class MutateInResult:
330
def content_as(self, index: int, target_type: type):
331
"""
332
Get content of mutation operation at index.
333
334
Args:
335
index (int): Operation index
336
target_type (type): Target type for content
337
338
Returns:
339
Content converted to target type
340
"""
341
342
@property
343
def cas(self) -> int:
344
"""New document CAS value."""
345
346
@property
347
def mutation_token(self) -> MutationToken:
348
"""Mutation token for consistency."""
349
```
350
351
## Usage Examples
352
353
### Basic Subdocument Operations
354
355
```python
356
import couchbase.subdocument as SD
357
358
# Document structure
359
doc = {
360
"name": "John Doe",
361
"age": 30,
362
"address": {
363
"street": "123 Main St",
364
"city": "San Francisco"
365
},
366
"hobbies": ["reading", "cycling"]
367
}
368
369
collection.upsert("user::123", doc)
370
371
# Lookup specific fields
372
result = collection.lookup_in("user::123", [
373
SD.get("name"),
374
SD.get("address.city"),
375
SD.exists("phone"),
376
SD.count("hobbies")
377
])
378
379
name = result.content_as(0, str)
380
city = result.content_as(1, str)
381
has_phone = result.exists(2)
382
hobby_count = result.content_as(3, int)
383
384
print(f"Name: {name}, City: {city}")
385
print(f"Has phone: {has_phone}, Hobbies: {hobby_count}")
386
```
387
388
### Subdocument Mutations
389
390
```python
391
# Update specific fields
392
collection.mutate_in("user::123", [
393
SD.replace("age", 31),
394
SD.upsert("address.zipcode", "94105"),
395
SD.array_append("hobbies", "photography"),
396
SD.increment("login_count", 1)
397
])
398
399
# Insert new nested object
400
collection.mutate_in("user::123", [
401
SD.insert("preferences", {"theme": "dark", "notifications": True})
402
])
403
404
# Remove field
405
collection.mutate_in("user::123", [
406
SD.remove("temporary_field")
407
])
408
```
409
410
### Array Operations
411
412
```python
413
# Working with arrays
414
collection.mutate_in("user::123", [
415
SD.array_append("hobbies", "gaming", "cooking"),
416
SD.array_prepend("hobbies", "traveling"),
417
SD.array_insert("hobbies[2]", "swimming"),
418
SD.array_add_unique("tags", "vip")
419
])
420
421
# Check array contents
422
result = collection.lookup_in("user::123", [
423
SD.get("hobbies"),
424
SD.count("hobbies"),
425
SD.get("hobbies[0]") # First element
426
])
427
428
all_hobbies = result.content_as(0, list)
429
hobby_count = result.content_as(1, int)
430
first_hobby = result.content_as(2, str)
431
```
432
433
### Counter Operations
434
435
```python
436
# Initialize counters
437
collection.mutate_in("stats::global", [
438
SD.upsert("page_views", 0, create_path=True),
439
SD.upsert("user_count", 100, create_path=True)
440
], MutateInOptions(upsert_document=True))
441
442
# Increment counters atomically
443
collection.mutate_in("stats::global", [
444
SD.increment("page_views", 1),
445
SD.increment("user_count", 1),
446
SD.increment("api_calls", 5)
447
])
448
449
# Get current values
450
result = collection.lookup_in("stats::global", [
451
SD.get("page_views"),
452
SD.get("user_count")
453
])
454
455
views = result.content_as(0, int)
456
users = result.content_as(1, int)
457
```
458
459
### Document Creation with Subdocument
460
461
```python
462
# Create document using subdocument operations
463
collection.mutate_in("user::456", [
464
SD.upsert("name", "Alice Smith"),
465
SD.upsert("profile.bio", "Software engineer"),
466
SD.upsert("profile.skills", ["Python", "JavaScript"]),
467
SD.upsert("stats.login_count", 0)
468
], MutateInOptions(upsert_document=True))
469
```
470
471
### Extended Attributes (XAttrs)
472
473
```python
474
# Work with extended attributes (metadata)
475
collection.mutate_in("user::123", [
476
SD.upsert("_metadata.created_by", "system", xattr=True),
477
SD.upsert("_metadata.version", 1, xattr=True),
478
SD.replace("name", "John Smith") # Regular document update
479
])
480
481
# Lookup extended attributes
482
result = collection.lookup_in("user::123", [
483
SD.get("_metadata", xattr=True),
484
SD.get("name")
485
])
486
487
metadata = result.content_as(0, dict)
488
name = result.content_as(1, str)
489
```
490
491
### Error Handling
492
493
```python
494
from couchbase.exceptions import PathNotFoundException, PathExistsException
495
496
try:
497
collection.mutate_in("user::123", [
498
SD.replace("nonexistent.field", "value"),
499
SD.insert("existing.field", "new_value")
500
])
501
except PathNotFoundException as e:
502
print(f"Path not found: {e}")
503
except PathExistsException as e:
504
print(f"Path already exists: {e}")
505
```