or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-operations.mdindex.mdindexing-system.mdquerying-views.mdstorage-backends.md
tile.json

querying-views.mddocs/

Data Querying and Views

The KVStore provides powerful querying capabilities through the KVStoreView and KVStoreIterator interfaces. These allow for efficient data retrieval with filtering, sorting, pagination, and range queries without loading all data into memory.

Capabilities

KVStoreView

Configurable view for iterating over values in a KVStore with support for sorting, filtering, and pagination.

/**
 * A configurable view that allows iterating over values in a KVStore.
 * Methods can be chained to configure iteration behavior.
 */
public abstract class KVStoreView<T> implements Iterable<T> {
    
    /**
     * Reverses the order of iteration. By default, iterates in ascending order.
     * @return This view instance for method chaining
     */
    public KVStoreView<T> reverse();
    
    /**
     * Iterates according to the given index.
     * @param name The name of the index to use for iteration
     * @return This view instance for method chaining
     */  
    public KVStoreView<T> index(String name);
    
    /**
     * Defines the value of the parent index when iterating over a child index.
     * Required for iterating over child indices.
     * @param value The parent index value to match
     * @return This view instance for method chaining
     */
    public KVStoreView<T> parent(Object value);
    
    /**
     * Iterates starting at the given value of the chosen index (inclusive).
     * @param value The starting value for iteration
     * @return This view instance for method chaining
     */
    public KVStoreView<T> first(Object value);
    
    /**
     * Stops iteration at the given value of the chosen index (inclusive).
     * @param value The ending value for iteration
     * @return This view instance for method chaining
     */
    public KVStoreView<T> last(Object value);
    
    /**
     * Limits iteration to a maximum number of elements.
     * @param max Maximum number of elements to retrieve (must be positive)
     * @return This view instance for method chaining
     */
    public KVStoreView<T> max(long max);
    
    /**
     * Skips a number of elements at the start of iteration.
     * Skipped elements are not counted against max().
     * @param n Number of elements to skip
     * @return This view instance for method chaining
     */
    public KVStoreView<T> skip(long n);
    
    /**
     * Returns an iterator for the current configuration.
     * @return A closeable iterator instance
     * @throws Exception if iterator creation fails
     */
    public KVStoreIterator<T> closeableIterator() throws Exception;
}

KVStoreIterator

Enhanced iterator interface providing batch operations and resource management for efficient data access.

/**
 * An iterator for KVStore with resource management and batch operations.
 * Should be explicitly closed after use unless all elements are consumed.
 */
public interface KVStoreIterator<T> extends Iterator<T>, Closeable {
    
    /**
     * Retrieve multiple elements from the store in a single operation.
     * @param max Maximum number of elements to retrieve
     * @return List of up to 'max' elements
     */
    List<T> next(int max);
    
    /**
     * Skip elements in the iterator efficiently.
     * @param n Number of elements to skip
     * @return true if there are items left after skipping, false otherwise
     */
    boolean skip(long n);
}

Usage Examples:

import org.apache.spark.util.kvstore.*;
import java.util.List;

// Assume we have a Task class with indexed fields
public class Task {
    @KVIndex
    public String id;
    
    @KVIndex("priority")
    public int priority;
    
    @KVIndex("status")
    public String status;
    
    @KVIndex("userId")
    public String userId;
    
    public Task(String id, int priority, String status, String userId) {
        this.id = id;
        this.priority = priority;
        this.status = status;
        this.userId = userId;
    }
}

// Basic iteration over all tasks
KVStoreView<Task> allTasks = store.view(Task.class);
for (Task task : allTasks) {
    System.out.println("Task: " + task.id);
}

// Query high priority tasks (priority >= 8) in descending order
KVStoreView<Task> highPriorityTasks = store.view(Task.class)
    .index("priority")
    .first(8)
    .reverse();

for (Task task : highPriorityTasks) {
    System.out.println("High priority task: " + task.id + " (priority: " + task.priority + ")");
}

// Get first 10 running tasks, skipping the first 5
KVStoreView<Task> runningTasks = store.view(Task.class)
    .index("status")
    .first("running")
    .last("running")
    .skip(5)
    .max(10);

// Using closeable iterator for resource management
try (KVStoreIterator<Task> iterator = runningTasks.closeableIterator()) {
    while (iterator.hasNext()) {
        Task task = iterator.next();
        System.out.println("Running task: " + task.id);
    }
}

// Batch operations with iterator
try (KVStoreIterator<Task> iterator = store.view(Task.class).closeableIterator()) {
    List<Task> batch;
    while (!(batch = iterator.next(100)).isEmpty()) {
        processBatch(batch);
    }
}

// Range queries - tasks with priority between 5 and 9
KVStoreView<Task> mediumPriorityTasks = store.view(Task.class)
    .index("priority")
    .first(5)
    .last(9);

// Skip and pagination
KVStoreView<Task> page2Tasks = store.view(Task.class)
    .skip(20)  // Skip first 20 tasks
    .max(10);  // Get next 10 tasks

// Parent-child relationships (if using parent indices)
// Assuming we have a parent index relationship
KVStoreView<Task> userTasks = store.view(Task.class)
    .index("userId")
    .parent("user-123");

Query Patterns

Simple Queries:

// Get all items of a type
KVStoreView<Task> all = store.view(Task.class);

// Get items sorted by an index
KVStoreView<Task> byPriority = store.view(Task.class).index("priority");

// Reverse sort order
KVStoreView<Task> descending = store.view(Task.class).index("priority").reverse();

Range Queries:

// Items with index value >= start
KVStoreView<Task> fromStart = store.view(Task.class).index("priority").first(5);

// Items with index value <= end
KVStoreView<Task> toEnd = store.view(Task.class).index("priority").last(9);

// Items with index value between start and end (inclusive)
KVStoreView<Task> range = store.view(Task.class).index("priority").first(5).last(9);

// Exact match (start and end are the same)
KVStoreView<Task> exact = store.view(Task.class).index("status").first("running").last("running");

Pagination:

// First page (items 1-20)
KVStoreView<Task> page1 = store.view(Task.class).max(20);

// Second page (items 21-40)
KVStoreView<Task> page2 = store.view(Task.class).skip(20).max(20);

// Third page (items 41-60)
KVStoreView<Task> page3 = store.view(Task.class).skip(40).max(20);

Resource Management:

// Automatic resource cleanup with enhanced for loop
for (Task task : store.view(Task.class)) {
    // Iterator is automatically closed when loop completes
    processTask(task);
}

// Manual resource management for partial iteration
KVStoreIterator<Task> iterator = store.view(Task.class).closeableIterator();
try {
    if (iterator.hasNext()) {
        Task first = iterator.next();
        // Process only the first task, then exit
        return processTask(first);
    }
} finally {
    iterator.close(); // Important: close manually when not exhausting iterator
}

// Try-with-resources for automatic cleanup
try (KVStoreIterator<Task> iterator = store.view(Task.class).closeableIterator()) {
    while (iterator.hasNext()) {
        Task task = iterator.next();
        if (shouldStop(task)) {
            break; // Iterator will be closed automatically
        }
        processTask(task);
    }
}