Repositories

EntityRepository gives you CRUD, batch operations, custom queries, and cursor streaming — all as suspend functions.

Defining a repository

@Vault
data class BlogPost(
    val title: String,
    val content: String,
    val author: String,
    val published: Boolean = false,
)

class BlogPostsRepo(driver: KarangoDriver) : EntityRepository<BlogPost>(
    name = "blog_posts",
    storedType = kType(),
    driver = driver,
)

That's it. You get full CRUD, queries, batch operations, and index management for free.

Insert

val post = BlogPost("Hello World", "My first post", "Alice")

// Insert and get the stored version (with _id, _key, _rev)
val stored: Stored<BlogPost> = repo.insert(post)

println(stored._id)         // "blog_posts/abc123"
println(stored._key)        // "abc123"
println(stored().title) // "Hello World"

// Insert with explicit key
val stored2 = repo.insert("my-key", post)

Find

// By ID
val post: Stored<BlogPost>? = repo.findById("blog_posts/abc123")

// By multiple IDs
val posts: Cursor<Stored<BlogPost>> = repo.findByIds("id1", "id2", "id3")

// All documents
val all: Cursor<Stored<BlogPost>> = repo.findAll()

// Custom query — returns a cursor
val published = repo.find {
    FOR(repo) { post ->
        FILTER(post.published EQ true)
        SORT(post.title.ASC)
        RETURN(post)
    }
}

// Convenience: returns a List directly
val list = repo.findList {
    FOR(repo) { post ->
        FILTER(post.author EQ "Alice")
        RETURN(post)
    }
}

// Convenience: returns the first match
val first = repo.findFirst {
    FOR(repo) { post ->
        FILTER(post.title EQ "Hello World")
        LIMIT(1)
        RETURN(post)
    }
}

Update

// Save a modified entity
val updated = repo.save(stored.modify { it.copy(published = true) })

// Modify by ID — atomic read-modify-write
repo.modifyById(stored._id) { post ->
    post.copy(published = true)
}

// Conditional modify — only if the condition holds
// condition receives Stored<T>, modify receives T
repo.modifyByIdWhen(
    stored._id,
    condition = { !it().published },
    modify = { it.copy(published = true) },
)

Remove

// By stored entity
repo.remove(stored)

// By ID or key
repo.remove("blog_posts/abc123")

// Remove all documents
repo.removeAll()

Batch insert

val posts = listOf(
    BlogPost("Post 1", "Content 1", "Alice"),
    BlogPost("Post 2", "Content 2", "Bob"),
    BlogPost("Post 3", "Content 3", "Alice"),
)

val stored: List<Stored<BlogPost>> = repo.batchInsertValues(posts)

Batch insert runs hooks on each entity, just like single inserts.

Cursors

Queries return Cursor<T>, a Flow-based wrapper. Use asFlow() for streaming or the suspend convenience extensions for common operations:

val cursor = repo.findAll()

cursor.count      // number of results in current batch
cursor.fullCount  // total count (when using PAGE)

// Collect all results
cursor.toList()                                      // suspend

// Iterate
cursor.forEach { stored -> println(stored().title) }  // suspend

// Transform and filter (all suspend)
cursor.map { it().title }
cursor.filter { it().published }
cursor.firstOrNull()
cursor.groupBy { it().author }

// Flow-based streaming
cursor.asFlow().collect { ... }

Results are deserialized lazily. Stored<T> entities are automatically cached in the EntityCache for deduplication. All cursor convenience methods (map, filter, forEach, toList, etc.) are suspend functions.

The Stored<T> wrapper

Every entity loaded from ArangoDB is wrapped in Stored<T>:

val stored: Stored<BlogPost> = repo.findById(id)!!

stored._id     // ArangoDB document ID: "blog_posts/abc123"
stored._key    // Document key: "abc123"
stored._rev    // Revision: "_abc123"
stored()       // The actual BlogPost data class

// Create a modified version
val modified = stored.modify { it.copy(title = "New Title") }

// Save it back
repo.save(modified)

Accessing the repository from Database

Use extension properties to access repositories through the Database:

// Define an accessor
val Database.blogPosts get() = getRepository<BlogPostsRepo>()

// Use it
database.blogPosts.findAll()
database.blogPosts.insert(New(post))