velesdb-mobile 3.3.0

VelesDB mobile bindings for iOS and Android via UniFFI
Documentation

VelesDB Mobile

Last updated: 2026-06-14.

Native bindings for iOS (Swift) and Android (Kotlin) via UniFFI.

VelesDB Mobile brings microsecond vector search to edge devices - perfect for on-device AI, semantic search, and RAG applications.

Features

  • Native Performance: Direct Rust bindings, minimal FFI overhead
  • Multi-Query Fusion: Native MQG with RRF/Weighted strategies
  • Binary Quantization: 32x memory reduction for constrained devices
  • ARM NEON SIMD: Optimized for mobile processors (Apple A-series, Snapdragon)
  • Offline-First: Full functionality without network connectivity
  • Thread-Safe: Safe to use from multiple threads/queues

Quick Start

Swift (iOS)

import VelesDB

// Open database (UniFFI named constructor — Rust `#[uniffi::constructor] open` becomes a Swift static method, not a default init)
let db = try VelesDatabase.open(path: documentsPath + "/velesdb")

// Create collection (768D for MiniLM, 384D for all-MiniLM-L6-v2)
try db.createCollection(name: "documents", dimension: 384, metric: .cosine)

// Get collection
guard let collection = try db.getCollection(name: "documents") else {
    fatalError("Collection not found")
}

// Insert vectors
let point = VelesPoint(
    id: 1,
    vector: embedding,  // [Float] from your embedding model
    payload: "{\"title\": \"Hello World\"}"
)
try collection.upsert(point: point)

// Search
let results = try collection.search(vector: queryEmbedding, limit: 10)
for result in results {
    print("ID: \(result.id), Score: \(result.score)")
}

Kotlin (Android)

import com.velesdb.mobile.*

// Open database (UniFFI named constructor — Rust `#[uniffi::constructor] open` becomes a Kotlin companion-object factory, not a default constructor)
val db = VelesDatabase.open("${context.filesDir}/velesdb")

// Create collection
db.createCollection("documents", 384u, DistanceMetric.COSINE)

// Get collection
val collection = db.getCollection("documents")
    ?: throw Exception("Collection not found")

// Insert vectors
val point = VelesPoint(
    id = 1uL,
    vector = embedding,  // List<Float> from your embedding model
    payload = """{"title": "Hello World"}"""
)
collection.upsert(point)

// Search (use Dispatchers.IO for async)
val results = withContext(Dispatchers.IO) {
    collection.search(queryEmbedding, 10u)
}
results.forEach { result ->
    println("ID: ${result.id}, Score: ${result.score}")
}

Build Instructions

Prerequisites

# Install Rust targets
rustup target add aarch64-apple-ios        # iOS device
rustup target add aarch64-apple-ios-sim    # iOS simulator (ARM)
rustup target add x86_64-apple-ios         # iOS simulator (Intel)

rustup target add aarch64-linux-android    # Android ARM64
rustup target add armv7-linux-androideabi  # Android ARMv7
rustup target add x86_64-linux-android     # Android x86_64

# For Android: Install cargo-ndk
cargo install cargo-ndk

iOS Build

# Build for device
cargo build --release --target aarch64-apple-ios -p velesdb-mobile

# Build for simulator
cargo build --release --target aarch64-apple-ios-sim -p velesdb-mobile

# Generate Swift bindings
cargo run -p velesdb-mobile --bin uniffi-bindgen -- generate \
    --library target/aarch64-apple-ios/release/libvelesdb_mobile.a \
    --language swift \
    --out-dir bindings/swift

# Create XCFramework (requires macOS)
xcodebuild -create-xcframework \
    -library target/aarch64-apple-ios/release/libvelesdb_mobile.a \
    -headers bindings/swift \
    -library target/aarch64-apple-ios-sim/release/libvelesdb_mobile.a \
    -headers bindings/swift \
    -output VelesDB.xcframework

Android Build

# Build for all Android ABIs
cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 \
    build --release -p velesdb-mobile

# Generate Kotlin bindings
cargo run -p velesdb-mobile --bin uniffi-bindgen -- generate \
    --library target/aarch64-linux-android/release/libvelesdb_mobile.so \
    --language kotlin \
    --out-dir bindings/kotlin

# Libraries are in:
# - target/aarch64-linux-android/release/libvelesdb_mobile.so
# - target/armv7-linux-androideabi/release/libvelesdb_mobile.so
# - target/x86_64-linux-android/release/libvelesdb_mobile.so

API Reference

VelesDatabase

Method Description
VelesDatabase.open(path) Opens or creates a database at the specified path (named constructor — use .open(...))
createCollection(name, dimension, metric) Creates a new vector collection
createCollectionWithStorage(name, dimension, metric, storageMode) Creates collection with quantized storage
createMetadataCollection(name) Creates a metadata-only collection (no vectors)
getCollection(name) Gets a collection by name (returns nil/null if not found)
listCollections() Lists all collection names
deleteCollection(name) Deletes a collection
trainPq(collectionName, config) Trains Product Quantization on a collection

VelesCollection

Method Description
search(vector, limit) Finds k nearest neighbors
searchWithFilter(vector, limit, filterJson) Search with metadata filter
multiQuerySearch(vectors, limit, strategy) Multi-query fusion (MQG)
multiQuerySearchWithFilter(vectors, limit, strategy, filterJson) Multi-query fusion with metadata filter
textSearch(query, limit) BM25 full-text search
textSearchWithFilter(query, limit, filterJson) Text search with filter
hybridSearch(vector, textQuery, limit, vectorWeight) Combined vector + text search
hybridSearchWithFilter(vector, textQuery, limit, vectorWeight, filterJson) Hybrid search with metadata filter
batchSearch(searches) Batch search with individual filters per query
sparseSearch(sparseVector, limit, indexName) Sparse-only search using inverted index
hybridSparseSearch(vector, sparseVector, limit, indexName) Hybrid dense + sparse search with RRF fusion
query(queryStr, paramsJson) Execute VelesQL query
upsert(point) Inserts or updates a single point
upsertBatch(points) Batch insert/update (faster for bulk operations)
upsertWithSparse(point, sparseVector) Inserts a point with an associated sparse vector
enableStreaming(config) Enables streaming ingestion (config optional; defaults bufferSize=10000, batchSize=128, flushIntervalMs=50)
streamInsert(points) Queues a batch of points for streaming ingestion; returns the count queued
delete(id) Deletes a point by ID
get(ids) Gets points by their IDs (missing IDs silently skipped)
getById(id) Gets a single point by ID (returns nil/null if not found)
count() Returns the number of points
dimension() Returns the vector dimension
isMetadataOnly() Checks if this is a metadata-only collection
allIds() Returns all point IDs in the collection
flush() Flushes data to durable storage
createIndex(fieldName) Creates a secondary metadata index
hasSecondaryIndex(fieldName) Checks if a secondary index exists
createPropertyIndex(label, property) Creates a graph/property index
createRangeIndex(label, property) Creates a graph/range index
hasPropertyIndex(label, property) Checks if a property index exists
hasRangeIndex(label, property) Checks if a range index exists
listIndexes() Lists all index definitions
dropIndex(label, property) Drops an index
indexesMemoryUsage() Returns memory used by indexes (bytes)
analyze() Runs ANALYZE and returns fresh statistics
getStats() Returns the latest statistics snapshot

The filterJson shape

The filterJson argument on searchWithFilter, textSearchWithFilter, hybridSearchWithFilter, and multiQuerySearchWithFilter is a JSON string using the same canonical filter shape as the core engine and REST API: {"condition": {"type": <op>, "field": ..., "value"/"values"/"pattern"/"conditions": ...}}. Operators: eq, neq, gt, gte, lt, lte, in, contains, like, ilike, is_null, is_not_null, array_contains, array_contains_any, array_contains_all, geo_distance, geo_bbox, and and/or/not for composition.

Swift:

let results = try collection.searchWithFilter(
    vector: queryVector,
    limit: 5,
    filterJson: #"{"condition": {"type": "eq", "field": "category", "value": "tech"}}"#
)

Kotlin:

val results = collection.searchWithFilter(
    queryVector,
    5,
    """{"condition": {"type": "eq", "field": "category", "value": "tech"}}"""
)

VelesSemanticMemory

Agent memory for on-device AI. Stores knowledge facts as vectors with similarity search.

Method Description
VelesSemanticMemory(db, dimension) Creates semantic memory with the given embedding dimension (constructor)
store(id, content, embedding) Stores a knowledge fact with its embedding
query(embedding, topK) Queries by similarity, returns SemanticResult list
delete(id) Deletes a knowledge fact by ID
remove(id) Deprecated alias for delete(id)
clear() Clears all knowledge facts
len() Returns the number of stored facts
isEmpty() Returns true if no facts are stored
dimension() Returns the embedding dimension

MobileGraphStore

In-memory graph store for mobile knowledge graphs.

Method Description
MobileGraphStore() Creates a new empty graph store (constructor)
addNode(node) Adds a node to the graph
addEdge(edge) Adds an edge (returns error if duplicate ID)
getNode(id) Gets a node by ID
getEdge(id) Gets an edge by ID
hasNode(id) Checks if a node exists
hasEdge(id) Checks if an edge exists
nodeCount() Returns the number of nodes
edgeCount() Returns the number of edges
getOutgoing(nodeId) Gets outgoing edges from a node
getIncoming(nodeId) Gets incoming edges to a node
getOutgoingByLabel(nodeId, label) Gets outgoing edges filtered by label
getNeighbors(nodeId) Gets neighbor node IDs (1-hop)
getNodesByLabel(label) Gets all nodes with a specific label
getEdgesByLabel(label) Gets all edges with a specific label
outDegree(nodeId) Returns the out-degree of a node
inDegree(nodeId) Returns the in-degree of a node
bfsTraverse(sourceId, maxDepth, limit) Breadth-first traversal
bfsTraverseParallel(sourceIds, maxDepth, limit) Multi-source parallel BFS with deduplication
dfsTraverse(sourceId, maxDepth, limit) Depth-first traversal
removeNode(nodeId) Removes a node and all connected edges
removeEdge(edgeId) Removes an edge by ID
clear() Clears all nodes and edges

Distance Metrics

Metric Description Use Case
Cosine Cosine similarity (1 - cosine_distance) Text embeddings, normalized vectors
Euclidean L2 distance Image features, unnormalized vectors
DotProduct Dot product Pre-normalized vectors, MaxSim
Hamming Hamming distance for binary vectors Binary embeddings, LSH
Jaccard Jaccard similarity for sets Sparse vectors, tags

Storage Modes (IoT/Edge)

Mode Compression Memory/dim Recall Loss Use Case
Full 1x 4 bytes 0% Best quality
Sq8 4x 1 byte ~1% Recommended for mobile
Binary 32x 1 bit ~5-10% Extreme constraints (IoT)
// iOS - Create collection with SQ8 compression (4x memory reduction)
try db.createCollectionWithStorage(
    name: "embeddings",
    dimension: 384,
    metric: .cosine,
    storageMode: .sq8  // 4x less memory, ~1% recall loss
)
// Android - Binary quantization for IoT devices (32x compression)
db.createCollectionWithStorage(
    "embeddings", 384u, DistanceMetric.COSINE, StorageMode.BINARY
)

Fusion Strategies

Used with multiQuerySearch() for combining results from multiple query vectors.

Strategy Description
Average Average scores across all queries
Maximum Take the maximum score per document
Rrf(k) Reciprocal Rank Fusion (default k=60)
Weighted(avgWeight, maxWeight, hitWeight) Weighted combination of avg, max, and hit ratio

Data Types

Type Fields Description
VelesPoint id: UInt64, vector: [Float], payload: String? A point to insert
SearchResult id: UInt64, score: Float A search result
SemanticResult id: UInt64, score: Float, content: String Semantic memory result
VelesSparseVector indices: [UInt32], values: [Float] Sparse vector (parallel arrays)
IndividualSearchRequest vector: [Float], topK: UInt32, filter: String? Batch search request
PqTrainConfig m: UInt32, k: UInt32, opq: Bool PQ training configuration
MobileGraphNode id: UInt64, label: String, propertiesJson: String?, vector: [Float]? Graph node
MobileGraphEdge id: UInt64, source: UInt64, target: UInt64, label: String, propertiesJson: String? Graph edge
TraversalResult nodeId: UInt64, path: [UInt64], depth: UInt32 BFS/DFS traversal result (path = edge IDs taken from the source; mirrors core's TraversalResult)
MobileCollectionStats totalPoints, payloadSizeBytes, rowCount, ... Collection statistics
MobileIndexInfo label, property, indexType, cardinality, memoryBytes Index metadata

Performance Tips

  1. Use SQ8 or Binary Quantization for memory-constrained devices
  2. Batch inserts with upsertBatch() for 10x faster bulk loading
  3. Use search() on background thread to avoid blocking UI
  4. Pre-allocate embedding arrays to reduce allocations

Memory Footprint

Vectors Dimension Storage Mode Memory
10,000 384 Full (f32) ~15 MB
10,000 384 SQ8 ~4 MB
10,000 384 Binary ~0.5 MB
100,000 768 Full (f32) ~300 MB
100,000 768 Binary ~10 MB

License

Licensed under the VelesDB Core License 1.0 (source-available). The compiled mobile bindings embed the VelesDB engine and are governed by the Core License.