Indexes & Hooks
Define indexes with the same type-safe property paths you use in queries. Hook into entity lifecycle for timestamps, validation, and side effects.
Defining indexes
Override buildIndexes() in your repository. Index fields use the same
KSP-generated property paths as queries.
class PersonsRepo(driver: MonkoDriver) : MonkoRepository<Person>(
name = "persons",
storedType = kType(),
driver = driver,
) {
override fun MonkoIndexBuilder<Person>.buildIndexes() {
// Single field index
persistentIndex {
field { it.email }
}
// Compound index
persistentIndex {
field { it.address.city }
field { it.address.zip }
}
// Unique index with custom name
uniqueIndex {
name("idx_email_unique")
field { it.email }
}
// TTL index — auto-delete expired documents
ttlIndex {
field { it.expiresAt }
expireAfter(0) // delete when expiresAt is in the past
}
// Sparse index — only index documents where the field exists
sparseIndex {
field { it.middleName }
}
}
} Index types
| Builder | MongoDB Type | Use case |
|---|---|---|
persistentIndex | Standard index | Query optimization |
uniqueIndex | Unique index | Enforce uniqueness |
ttlIndex | TTL index | Auto-expire documents |
sparseIndex | Sparse index | Index only non-null fields |
Index management
// Ensure indexes exist (create if missing, skip if present)
repo.ensureIndexes()
// Drop and recreate all indexes
repo.recreateIndexes()
// Validate without creating
val info = repo.validateIndexes() On application startup, Funktor calls
ensureIndexes() automatically via the
EnsureRepositoriesOnAppStarting lifecycle hook. You don't need to manage this manually.
Lifecycle hooks
Hooks run during entity lifecycle events. They use the same Vault hook interfaces as Karango.
Timestamped
The most common hook. Automatically sets createdAt and updatedAt.
@Vault
data class Article(
val title: String,
val body: String,
override val createdAt: MpInstant = MpInstant.Epoch,
override val updatedAt: MpInstant = MpInstant.Epoch,
) : Timestamped {
override fun withCreatedAt(instant: MpInstant) = copy(createdAt = instant)
override fun withUpdatedAt(instant: MpInstant) = copy(updatedAt = instant)
} Custom hooks
class ArticlesRepo(driver: MonkoDriver) : MonkoRepository<Article>(
name = "articles",
storedType = kType(),
driver = driver,
hooks = Hooks.of(
onBeforeSave = listOf(TimestampedOnBeforeSaveHook()),
onAfterSave = listOf(MyAfterSaveHook()),
onAfterDelete = listOf(MyAfterDeleteHook()),
),
) Hook execution order
| Operation | Hook sequence |
|---|---|
insert() | OnBeforeSave → persist → OnAfterSave |
save() | OnBeforeSave → persist → OnAfterSave |
remove() | delete → OnAfterDelete |