Maven Central
Kotlin and Android local retrieval guide
Install the JVM or Android artifact, run typed JNI retrieval, scope with graph selections, and manage deterministic lifetimes.
On this page
Install
Choose exactly one base or graph retrieval artifact. Graph already includes retrieval. Embedding is independent.
dependencies {
// JVM graph plus retrieval
implementation("io.github.gungorbasa:retrievalkit-graph:0.1.0")
implementation("io.github.gungorbasa:retrievalkit-embedding:0.1.0")
// Android equivalents
implementation("io.github.gungorbasa:retrievalkit-graph-android:0.1.0")
implementation("io.github.gungorbasa:retrievalkit-embedding-android:0.1.0")
}
The JVM artifact contains a macOS arm64 native library. JDK 17 builds the
wrapper and the produced bytecode runs on Java 11+. Android artifacts target
API 24+ on arm64-v8a.
Android packaging is qualified. Live-device inference, lifecycle, compatibility, thermal behavior, and performance remain unqualified.
Build and search
Kotlin exposes blocking JNI calls. Run build, persistence, and search work on an application-selected background executor or coroutine dispatcher on Android.
import ai.retrievalkit.Document
import ai.retrievalkit.MetadataValue
import ai.retrievalkit.RetrievalDatabase
val database = RetrievalDatabase.Builder(corpusId = "apollo").use { builder ->
builder.upsert(
Document(
id = "decision-swift",
text = "Apollo chose Swift for native platform integration.",
metadata = mapOf(
"project" to MetadataValue.Text("apollo"),
),
),
floatArrayOf(1f, 0f, 0f),
)
builder.build()
}
database.use {
val hits = it.search(
text = "Why did we choose Swift?",
embedding = floatArrayOf(1f, 0f, 0f),
alpha = 0.6f,
limit = 5,
)
println(hits.firstOrNull()?.documentId)
}
The first upsert fixes dimension in Rust. FloatArray carries embeddings;
metadata integers and timestamps use exact signed Long values.
Choose a search mode
search(embedding = ...)performs exact vector search.search(text = ...)performs BM25-only search.search(text = ..., embedding = ..., alpha = ...)performs hybrid search.
Metadata filters are hard constraints and are applied before the final top-k.
Scope with a graph
Graph selections are opaque AutoCloseable resources.
graphRetrievalDatabase.query(
GraphQuery(
GraphSeed.Nodes(listOf(GraphNodeId("Project", "apollo"))),
traversals = listOf(GraphTraversal("contains")),
),
).use { selection ->
val hits = graphRetrievalDatabase.search(
text = "native integration",
embedding = floatArrayOf(1f, 0f, 0f),
alpha = 0.6f,
within = selection,
)
println(hits.firstOrNull()?.documentId)
}
Rust owns schema validation, traversal, typed path provenance, projection filtering, stale-selection rejection, and ranking.
Run the JVM source quickstart
export JAVA_HOME=$(/usr/libexec/java_home -v 17)
export PATH="$JAVA_HOME/bin:$PATH"
cd wrappers/kotlin
./scripts/preflight.sh jvm
./scripts/build-native.sh jvm
./gradlew :example-retrieval:run
Source builds require Rust cargo. Android builds additionally require NDK 26
and the Rust aarch64-linux-android target.
Choose the database boundary
Three query paths
Compare the same job in Kotlin. These are separate APIs, not modes hidden behind one query.
Hybrid vector + BM25
RetrievalDatabaseRank the whole corpus with exact vector similarity and BM25.
val hits = retrievalDatabase.search(
text = "What did we decide about offline sync?",
embedding = queryEmbedding,
alpha = 0.6f,
limit = 5,
)Graph query alone
GraphDatabaseSelect records by relationship. No embedding or retrieval ranker is involved.
graphDatabase.query(
GraphQuery(
seed = GraphSeed.Nodes(
listOf(GraphNodeId("Notebook", "product-research")),
),
traversals = listOf(GraphTraversal("references")),
),
).use { selection ->
selection.snapshot.matches.forEach(::println)
}Graph + vector + BM25
GraphRetrievalDatabaseSelect candidates with the graph, then rank only that scope with vector + BM25.
graphRetrievalDatabase.query(
GraphQuery(
GraphSeed.Nodes(
listOf(GraphNodeId("Project", "mobile-app")),
),
traversals = listOf(GraphTraversal("contains")),
),
).use { selection ->
val hits = graphRetrievalDatabase.search(
text = "Why is cold start slow on Android?",
embedding = queryEmbedding,
alpha = 0.6f,
within = selection,
limit = 5,
)
}