CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-lmdb

Universal Python binding for the LMDB 'Lightning' Database

Pending
Overview
Eval results
Files

cursors.mddocs/

Cursor Operations and Iteration

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.

Capabilities

Cursor Lifecycle

Create and manage cursor resources with proper cleanup and transaction binding.

class Transaction:
    def cursor(self, db=None) -> Cursor:
        """
        Create cursor for database iteration.

        Parameters:
        - db: Database handle (uses transaction default if None)

        Returns:
        Cursor instance bound to transaction and database
        """

class Cursor:
    def close(self) -> None:
        """
        Close cursor and free resources.
        Cursors are automatically closed when transaction ends.
        """

Data Access

Access current cursor position data with key, value, and combined retrieval methods.

class Cursor:
    def key(self) -> bytes:
        """
        Get key at current cursor position.

        Returns:
        Key bytes

        Raises:
        Error if cursor not positioned at valid record
        """

    def value(self) -> bytes:
        """
        Get value at current cursor position.

        Returns:
        Value bytes

        Raises:
        Error if cursor not positioned at valid record
        """

    def item(self) -> tuple:
        """
        Get (key, value) tuple at current cursor position.

        Returns:
        Tuple of (key_bytes, value_bytes)

        Raises:
        Error if cursor not positioned at valid record
        """

    def get(self, key: bytes, default=None) -> bytes:
        """
        Get value for specified key using cursor.

        Parameters:
        - key: Key to lookup
        - default: Value returned if key not found

        Returns:
        Value bytes or default if key not found
        """

    def count(self) -> int:
        """
        Count duplicate values for current key.

        Returns:
        Number of duplicates for current key

        Note:
        Only meaningful for databases opened with dupsort=True
        """

Positioning and Navigation

Navigate through database records with precise positioning and movement operations.

class Cursor:
    def first(self) -> bool:
        """
        Position cursor at first record in database.

        Returns:
        True if record found, False if database empty
        """

    def last(self) -> bool:
        """
        Position cursor at last record in database.

        Returns:
        True if record found, False if database empty
        """

    def next(self) -> bool:
        """
        Move cursor to next record.

        Returns:
        True if moved to valid record, False if at end
        """

    def prev(self) -> bool:
        """
        Move cursor to previous record.

        Returns:
        True if moved to valid record, False if at beginning
        """

    def set_key(self, key: bytes) -> bool:
        """
        Position cursor at specified key.

        Parameters:
        - key: Key to locate

        Returns:
        True if exact key found, False otherwise
        """

    def set_range(self, key: bytes) -> bool:
        """
        Position cursor at first key >= specified key.

        Parameters:
        - key: Minimum key value

        Returns:
        True if positioned at valid record, False if no keys >= key
        """

Duplicate Key Navigation

Navigate through duplicate values for databases configured with dupsort=True.

class Cursor:
    def first_dup(self) -> bool:
        """
        Position at first duplicate of current key.

        Returns:
        True if positioned, False if no duplicates or not positioned
        """

    def last_dup(self) -> bool:
        """
        Position at last duplicate of current key.

        Returns:
        True if positioned, False if no duplicates or not positioned
        """

    def next_dup(self) -> bool:
        """
        Move to next duplicate of current key.

        Returns:
        True if moved to duplicate, False if no more duplicates
        """

    def prev_dup(self) -> bool:
        """
        Move to previous duplicate of current key.

        Returns:
        True if moved to duplicate, False if no previous duplicates
        """

    def next_nodup(self) -> bool:
        """
        Move to next key (skipping any remaining duplicates of current key).

        Returns:
        True if moved to different key, False if at end
        """

    def prev_nodup(self) -> bool:
        """
        Move to previous key (skipping any remaining duplicates of current key).

        Returns:
        True if moved to different key, False if at beginning
        """

    def set_key_dup(self, key: bytes, value: bytes) -> bool:
        """
        Position at specific key-value pair.

        Parameters:
        - key: Key to locate
        - value: Specific value for the key

        Returns:
        True if exact key-value pair found
        """

    def set_range_dup(self, key: bytes, value: bytes) -> bool:
        """
        Position at first occurrence of key with value >= specified value.

        Parameters:
        - key: Key to locate
        - value: Minimum value for the key

        Returns:
        True if positioned at valid record
        """

Data Modification

Modify database through cursor with positioned insert, update, and delete operations.

class Cursor:
    def put(self, key: bytes, value: bytes, dupdata: bool = True,
            overwrite: bool = True, append: bool = False) -> bool:
        """
        Store key-value pair at cursor position.

        Parameters:
        - key: Key bytes
        - value: Value bytes  
        - dupdata: Allow duplicate keys
        - overwrite: Replace existing value
        - append: Optimize for appending (key must be >= all existing keys)

        Returns:
        True if new key inserted, False if existing key updated
        """

    def delete(self, dupdata: bool = True) -> bool:
        """
        Delete record at current cursor position.

        Parameters:
        - dupdata: Delete only current duplicate (False deletes all duplicates)

        Returns:
        True if record deleted
        """

    def replace(self, key: bytes, value: bytes) -> bytes:
        """
        Replace value at cursor position and return old value.

        Parameters:
        - key: Key (must match current position)
        - value: New value

        Returns:
        Previous value
        """

    def pop(self, dupdata: bool = True) -> tuple:
        """
        Get current record and delete it.

        Parameters:
        - dupdata: Delete only current duplicate

        Returns:
        Tuple of (key, value) that was deleted
        """

Iterator Interface

Python iterator protocol support for convenient record traversal with flexible options.

class Cursor:
    def iternext(self, keys: bool = True, values: bool = True) -> Iterator:
        """
        Forward iterator from current position.

        Parameters:
        - keys: Include keys in iteration
        - values: Include values in iteration

        Yields:
        Keys, values, or (key, value) tuples based on parameters
        """

    def iternext_dup(self, keys: bool = True, values: bool = True) -> Iterator:
        """
        Forward iterator over duplicates of current key.

        Parameters:
        - keys: Include keys in iteration
        - values: Include values in iteration

        Yields:
        Records for current key only
        """

    def iternext_nodup(self, keys: bool = True, values: bool = True) -> Iterator:
        """
        Forward iterator skipping duplicate keys.

        Parameters:
        - keys: Include keys in iteration
        - values: Include values in iteration

        Yields:
        First record for each unique key
        """

    def iterprev(self, keys: bool = True, values: bool = True) -> Iterator:
        """
        Reverse iterator from current position.

        Parameters:
        - keys: Include keys in iteration
        - values: Include values in iteration

        Yields:
        Keys, values, or (key, value) tuples in reverse order
        """

    def iterprev_dup(self, keys: bool = True, values: bool = True) -> Iterator:
        """
        Reverse iterator over duplicates of current key.

        Parameters:
        - keys: Include keys in iteration
        - values: Include values in iteration

        Yields:
        Records for current key in reverse order
        """

    def iterprev_nodup(self, keys: bool = True, values: bool = True) -> Iterator:
        """
        Reverse iterator skipping duplicate keys.

        Parameters:
        - keys: Include keys in iteration
        - values: Include values in iteration

        Yields:
        Last record for each unique key in reverse order
        """

Bulk Operations

Efficient batch operations for high-performance data processing.

class Cursor:
    def putmulti(self, items: Iterable, dupdata: bool = True, 
                 overwrite: bool = True, append: bool = False) -> int:
        """
        Store multiple key-value pairs efficiently.

        Parameters:
        - items: Iterable of (key, value) tuples
        - dupdata: Allow duplicate keys
        - overwrite: Replace existing values
        - append: Optimize for sequential insertion

        Returns:
        Number of items successfully stored
        """

    def getmulti(self, keys: Iterable, dupdata: bool = False) -> list:
        """
        Retrieve multiple values by keys efficiently.

        Parameters:
        - keys: Iterable of key bytes
        - dupdata: Retrieve all duplicates for each key

        Returns:
        List of (key, value) tuples for found keys
        """

Usage Examples

Basic Cursor Iteration

import lmdb

env = lmdb.open('/path/to/database')

# Forward iteration over all records
with env.begin() as txn:
    cursor = txn.cursor()
    
    # Iterate using cursor as iterator
    for key, value in cursor:
        print(f"Key: {key}, Value: {value}")
    
    # Or iterate manually
    if cursor.first():
        print(f"First: {cursor.key()} = {cursor.value()}")
        
        while cursor.next():
            print(f"Next: {cursor.key()} = {cursor.value()}")

env.close()

Range Queries

import lmdb

env = lmdb.open('/path/to/database')

with env.begin() as txn:
    cursor = txn.cursor()
    
    # Find all keys starting with 'user:'
    start_key = b'user:'
    end_key = b'user;'  # ';' is next ASCII character after ':'
    
    # Position at first key >= start_key
    if cursor.set_range(start_key):
        while cursor.key() < end_key:
            key, value = cursor.item()
            print(f"User record: {key} = {value}")
            
            if not cursor.next():
                break
    
    # Range query with specific bounds
    print("\\nUsers 100-199:")
    if cursor.set_range(b'user:100'):
        while cursor.key().startswith(b'user:1'):
            key, value = cursor.item()
            user_id = key[5:]  # Remove 'user:' prefix
            if user_id > b'199':
                break
            print(f"User {user_id}: {value}")
            
            if not cursor.next():
                break

env.close()

Working with Duplicates

import lmdb

# Open environment with duplicate support
env = lmdb.open('/path/to/database', max_dbs=1)
tags_db = env.open_db(b'post_tags', dupsort=True)

# Store posts with multiple tags
with env.begin(write=True) as txn:
    cursor = txn.cursor(db=tags_db)
    
    # Post 1 has multiple tags
    cursor.put(b'post:1', b'python')
    cursor.put(b'post:1', b'database')
    cursor.put(b'post:1', b'tutorial')
    
    # Post 2 has different tags
    cursor.put(b'post:2', b'javascript')
    cursor.put(b'post:2', b'web')

# Read all tags for a specific post
with env.begin() as txn:
    cursor = txn.cursor(db=tags_db)
    
    # Find all tags for post:1
    if cursor.set_key(b'post:1'):
        print(f"Tags for post:1:")
        print(f"  {cursor.value()}")  # First tag
        
        # Get remaining tags for same post
        while cursor.next_dup():
            print(f"  {cursor.value()}")
    
    # Iterate through all posts and their tags
    print("\\nAll posts and tags:")
    cursor.first()
    current_post = None
    
    for key, value in cursor:
        if key != current_post:
            current_post = key
            print(f"\\n{key}:")
        print(f"  {value}")

env.close()

Bulk Operations

import lmdb

env = lmdb.open('/path/to/database')

# Bulk insert
with env.begin(write=True) as txn:
    cursor = txn.cursor()
    
    # Prepare large dataset
    items = [(f'key{i:06d}'.encode(), f'value{i}'.encode()) 
             for i in range(10000)]
    
    # Efficient bulk insert
    count = cursor.putmulti(items, append=True)  # append=True for sequential keys
    print(f"Inserted {count} items")

# Bulk retrieval
with env.begin() as txn:
    cursor = txn.cursor()
    
    # Get specific keys
    keys_to_fetch = [f'key{i:06d}'.encode() for i in range(0, 1000, 100)]
    results = cursor.getmulti(keys_to_fetch)
    
    print(f"Retrieved {len(results)} items:")
    for key, value in results[:5]:  # Show first 5
        print(f"  {key} = {value}")

env.close()

Reverse Iteration

import lmdb

env = lmdb.open('/path/to/database')

with env.begin() as txn:
    cursor = txn.cursor()
    
    # Start from last record and work backwards
    if cursor.last():
        print("Reverse iteration:")
        print(f"Last: {cursor.key()} = {cursor.value()}")
        
        while cursor.prev():
            print(f"Prev: {cursor.key()} = {cursor.value()}")
    
    # Use reverse iterator
    print("\\nUsing reverse iterator:")
    cursor.last()
    for key, value in cursor.iterprev():
        print(f"{key} = {value}")

env.close()

Install with Tessl CLI

npx tessl i tessl/pypi-lmdb

docs

core-operations.md

cursors.md

index.md

multi-database.md

transactions.md

tile.json