0
# Cursor Operations and Iteration
1
2
Efficient database traversal and positioned access using cursors. Cursors provide powerful iteration capabilities, range queries, bulk operations, and precise positioning within the database's sorted key space.
3
4
## Capabilities
5
6
### Cursor Lifecycle
7
8
Create and manage cursor resources with proper cleanup and transaction binding.
9
10
```python { .api }
11
class Transaction:
12
def cursor(self, db=None) -> Cursor:
13
"""
14
Create cursor for database iteration.
15
16
Parameters:
17
- db: Database handle (uses transaction default if None)
18
19
Returns:
20
Cursor instance bound to transaction and database
21
"""
22
23
class Cursor:
24
def close(self) -> None:
25
"""
26
Close cursor and free resources.
27
Cursors are automatically closed when transaction ends.
28
"""
29
```
30
31
### Data Access
32
33
Access current cursor position data with key, value, and combined retrieval methods.
34
35
```python { .api }
36
class Cursor:
37
def key(self) -> bytes:
38
"""
39
Get key at current cursor position.
40
41
Returns:
42
Key bytes
43
44
Raises:
45
Error if cursor not positioned at valid record
46
"""
47
48
def value(self) -> bytes:
49
"""
50
Get value at current cursor position.
51
52
Returns:
53
Value bytes
54
55
Raises:
56
Error if cursor not positioned at valid record
57
"""
58
59
def item(self) -> tuple:
60
"""
61
Get (key, value) tuple at current cursor position.
62
63
Returns:
64
Tuple of (key_bytes, value_bytes)
65
66
Raises:
67
Error if cursor not positioned at valid record
68
"""
69
70
def get(self, key: bytes, default=None) -> bytes:
71
"""
72
Get value for specified key using cursor.
73
74
Parameters:
75
- key: Key to lookup
76
- default: Value returned if key not found
77
78
Returns:
79
Value bytes or default if key not found
80
"""
81
82
def count(self) -> int:
83
"""
84
Count duplicate values for current key.
85
86
Returns:
87
Number of duplicates for current key
88
89
Note:
90
Only meaningful for databases opened with dupsort=True
91
"""
92
```
93
94
### Positioning and Navigation
95
96
Navigate through database records with precise positioning and movement operations.
97
98
```python { .api }
99
class Cursor:
100
def first(self) -> bool:
101
"""
102
Position cursor at first record in database.
103
104
Returns:
105
True if record found, False if database empty
106
"""
107
108
def last(self) -> bool:
109
"""
110
Position cursor at last record in database.
111
112
Returns:
113
True if record found, False if database empty
114
"""
115
116
def next(self) -> bool:
117
"""
118
Move cursor to next record.
119
120
Returns:
121
True if moved to valid record, False if at end
122
"""
123
124
def prev(self) -> bool:
125
"""
126
Move cursor to previous record.
127
128
Returns:
129
True if moved to valid record, False if at beginning
130
"""
131
132
def set_key(self, key: bytes) -> bool:
133
"""
134
Position cursor at specified key.
135
136
Parameters:
137
- key: Key to locate
138
139
Returns:
140
True if exact key found, False otherwise
141
"""
142
143
def set_range(self, key: bytes) -> bool:
144
"""
145
Position cursor at first key >= specified key.
146
147
Parameters:
148
- key: Minimum key value
149
150
Returns:
151
True if positioned at valid record, False if no keys >= key
152
"""
153
```
154
155
### Duplicate Key Navigation
156
157
Navigate through duplicate values for databases configured with dupsort=True.
158
159
```python { .api }
160
class Cursor:
161
def first_dup(self) -> bool:
162
"""
163
Position at first duplicate of current key.
164
165
Returns:
166
True if positioned, False if no duplicates or not positioned
167
"""
168
169
def last_dup(self) -> bool:
170
"""
171
Position at last duplicate of current key.
172
173
Returns:
174
True if positioned, False if no duplicates or not positioned
175
"""
176
177
def next_dup(self) -> bool:
178
"""
179
Move to next duplicate of current key.
180
181
Returns:
182
True if moved to duplicate, False if no more duplicates
183
"""
184
185
def prev_dup(self) -> bool:
186
"""
187
Move to previous duplicate of current key.
188
189
Returns:
190
True if moved to duplicate, False if no previous duplicates
191
"""
192
193
def next_nodup(self) -> bool:
194
"""
195
Move to next key (skipping any remaining duplicates of current key).
196
197
Returns:
198
True if moved to different key, False if at end
199
"""
200
201
def prev_nodup(self) -> bool:
202
"""
203
Move to previous key (skipping any remaining duplicates of current key).
204
205
Returns:
206
True if moved to different key, False if at beginning
207
"""
208
209
def set_key_dup(self, key: bytes, value: bytes) -> bool:
210
"""
211
Position at specific key-value pair.
212
213
Parameters:
214
- key: Key to locate
215
- value: Specific value for the key
216
217
Returns:
218
True if exact key-value pair found
219
"""
220
221
def set_range_dup(self, key: bytes, value: bytes) -> bool:
222
"""
223
Position at first occurrence of key with value >= specified value.
224
225
Parameters:
226
- key: Key to locate
227
- value: Minimum value for the key
228
229
Returns:
230
True if positioned at valid record
231
"""
232
```
233
234
### Data Modification
235
236
Modify database through cursor with positioned insert, update, and delete operations.
237
238
```python { .api }
239
class Cursor:
240
def put(self, key: bytes, value: bytes, dupdata: bool = True,
241
overwrite: bool = True, append: bool = False) -> bool:
242
"""
243
Store key-value pair at cursor position.
244
245
Parameters:
246
- key: Key bytes
247
- value: Value bytes
248
- dupdata: Allow duplicate keys
249
- overwrite: Replace existing value
250
- append: Optimize for appending (key must be >= all existing keys)
251
252
Returns:
253
True if new key inserted, False if existing key updated
254
"""
255
256
def delete(self, dupdata: bool = True) -> bool:
257
"""
258
Delete record at current cursor position.
259
260
Parameters:
261
- dupdata: Delete only current duplicate (False deletes all duplicates)
262
263
Returns:
264
True if record deleted
265
"""
266
267
def replace(self, key: bytes, value: bytes) -> bytes:
268
"""
269
Replace value at cursor position and return old value.
270
271
Parameters:
272
- key: Key (must match current position)
273
- value: New value
274
275
Returns:
276
Previous value
277
"""
278
279
def pop(self, dupdata: bool = True) -> tuple:
280
"""
281
Get current record and delete it.
282
283
Parameters:
284
- dupdata: Delete only current duplicate
285
286
Returns:
287
Tuple of (key, value) that was deleted
288
"""
289
```
290
291
### Iterator Interface
292
293
Python iterator protocol support for convenient record traversal with flexible options.
294
295
```python { .api }
296
class Cursor:
297
def iternext(self, keys: bool = True, values: bool = True) -> Iterator:
298
"""
299
Forward iterator from current position.
300
301
Parameters:
302
- keys: Include keys in iteration
303
- values: Include values in iteration
304
305
Yields:
306
Keys, values, or (key, value) tuples based on parameters
307
"""
308
309
def iternext_dup(self, keys: bool = True, values: bool = True) -> Iterator:
310
"""
311
Forward iterator over duplicates of current key.
312
313
Parameters:
314
- keys: Include keys in iteration
315
- values: Include values in iteration
316
317
Yields:
318
Records for current key only
319
"""
320
321
def iternext_nodup(self, keys: bool = True, values: bool = True) -> Iterator:
322
"""
323
Forward iterator skipping duplicate keys.
324
325
Parameters:
326
- keys: Include keys in iteration
327
- values: Include values in iteration
328
329
Yields:
330
First record for each unique key
331
"""
332
333
def iterprev(self, keys: bool = True, values: bool = True) -> Iterator:
334
"""
335
Reverse iterator from current position.
336
337
Parameters:
338
- keys: Include keys in iteration
339
- values: Include values in iteration
340
341
Yields:
342
Keys, values, or (key, value) tuples in reverse order
343
"""
344
345
def iterprev_dup(self, keys: bool = True, values: bool = True) -> Iterator:
346
"""
347
Reverse iterator over duplicates of current key.
348
349
Parameters:
350
- keys: Include keys in iteration
351
- values: Include values in iteration
352
353
Yields:
354
Records for current key in reverse order
355
"""
356
357
def iterprev_nodup(self, keys: bool = True, values: bool = True) -> Iterator:
358
"""
359
Reverse iterator skipping duplicate keys.
360
361
Parameters:
362
- keys: Include keys in iteration
363
- values: Include values in iteration
364
365
Yields:
366
Last record for each unique key in reverse order
367
"""
368
```
369
370
### Bulk Operations
371
372
Efficient batch operations for high-performance data processing.
373
374
```python { .api }
375
class Cursor:
376
def putmulti(self, items: Iterable, dupdata: bool = True,
377
overwrite: bool = True, append: bool = False) -> int:
378
"""
379
Store multiple key-value pairs efficiently.
380
381
Parameters:
382
- items: Iterable of (key, value) tuples
383
- dupdata: Allow duplicate keys
384
- overwrite: Replace existing values
385
- append: Optimize for sequential insertion
386
387
Returns:
388
Number of items successfully stored
389
"""
390
391
def getmulti(self, keys: Iterable, dupdata: bool = False) -> list:
392
"""
393
Retrieve multiple values by keys efficiently.
394
395
Parameters:
396
- keys: Iterable of key bytes
397
- dupdata: Retrieve all duplicates for each key
398
399
Returns:
400
List of (key, value) tuples for found keys
401
"""
402
```
403
404
### Usage Examples
405
406
#### Basic Cursor Iteration
407
408
```python
409
import lmdb
410
411
env = lmdb.open('/path/to/database')
412
413
# Forward iteration over all records
414
with env.begin() as txn:
415
cursor = txn.cursor()
416
417
# Iterate using cursor as iterator
418
for key, value in cursor:
419
print(f"Key: {key}, Value: {value}")
420
421
# Or iterate manually
422
if cursor.first():
423
print(f"First: {cursor.key()} = {cursor.value()}")
424
425
while cursor.next():
426
print(f"Next: {cursor.key()} = {cursor.value()}")
427
428
env.close()
429
```
430
431
#### Range Queries
432
433
```python
434
import lmdb
435
436
env = lmdb.open('/path/to/database')
437
438
with env.begin() as txn:
439
cursor = txn.cursor()
440
441
# Find all keys starting with 'user:'
442
start_key = b'user:'
443
end_key = b'user;' # ';' is next ASCII character after ':'
444
445
# Position at first key >= start_key
446
if cursor.set_range(start_key):
447
while cursor.key() < end_key:
448
key, value = cursor.item()
449
print(f"User record: {key} = {value}")
450
451
if not cursor.next():
452
break
453
454
# Range query with specific bounds
455
print("\\nUsers 100-199:")
456
if cursor.set_range(b'user:100'):
457
while cursor.key().startswith(b'user:1'):
458
key, value = cursor.item()
459
user_id = key[5:] # Remove 'user:' prefix
460
if user_id > b'199':
461
break
462
print(f"User {user_id}: {value}")
463
464
if not cursor.next():
465
break
466
467
env.close()
468
```
469
470
#### Working with Duplicates
471
472
```python
473
import lmdb
474
475
# Open environment with duplicate support
476
env = lmdb.open('/path/to/database', max_dbs=1)
477
tags_db = env.open_db(b'post_tags', dupsort=True)
478
479
# Store posts with multiple tags
480
with env.begin(write=True) as txn:
481
cursor = txn.cursor(db=tags_db)
482
483
# Post 1 has multiple tags
484
cursor.put(b'post:1', b'python')
485
cursor.put(b'post:1', b'database')
486
cursor.put(b'post:1', b'tutorial')
487
488
# Post 2 has different tags
489
cursor.put(b'post:2', b'javascript')
490
cursor.put(b'post:2', b'web')
491
492
# Read all tags for a specific post
493
with env.begin() as txn:
494
cursor = txn.cursor(db=tags_db)
495
496
# Find all tags for post:1
497
if cursor.set_key(b'post:1'):
498
print(f"Tags for post:1:")
499
print(f" {cursor.value()}") # First tag
500
501
# Get remaining tags for same post
502
while cursor.next_dup():
503
print(f" {cursor.value()}")
504
505
# Iterate through all posts and their tags
506
print("\\nAll posts and tags:")
507
cursor.first()
508
current_post = None
509
510
for key, value in cursor:
511
if key != current_post:
512
current_post = key
513
print(f"\\n{key}:")
514
print(f" {value}")
515
516
env.close()
517
```
518
519
#### Bulk Operations
520
521
```python
522
import lmdb
523
524
env = lmdb.open('/path/to/database')
525
526
# Bulk insert
527
with env.begin(write=True) as txn:
528
cursor = txn.cursor()
529
530
# Prepare large dataset
531
items = [(f'key{i:06d}'.encode(), f'value{i}'.encode())
532
for i in range(10000)]
533
534
# Efficient bulk insert
535
count = cursor.putmulti(items, append=True) # append=True for sequential keys
536
print(f"Inserted {count} items")
537
538
# Bulk retrieval
539
with env.begin() as txn:
540
cursor = txn.cursor()
541
542
# Get specific keys
543
keys_to_fetch = [f'key{i:06d}'.encode() for i in range(0, 1000, 100)]
544
results = cursor.getmulti(keys_to_fetch)
545
546
print(f"Retrieved {len(results)} items:")
547
for key, value in results[:5]: # Show first 5
548
print(f" {key} = {value}")
549
550
env.close()
551
```
552
553
#### Reverse Iteration
554
555
```python
556
import lmdb
557
558
env = lmdb.open('/path/to/database')
559
560
with env.begin() as txn:
561
cursor = txn.cursor()
562
563
# Start from last record and work backwards
564
if cursor.last():
565
print("Reverse iteration:")
566
print(f"Last: {cursor.key()} = {cursor.value()}")
567
568
while cursor.prev():
569
print(f"Prev: {cursor.key()} = {cursor.value()}")
570
571
# Use reverse iterator
572
print("\\nUsing reverse iterator:")
573
cursor.last()
574
for key, value in cursor.iterprev():
575
print(f"{key} = {value}")
576
577
env.close()
578
```