couchbase-lite-ktx
Couchbase Lite Community Edition – Kotlin Extensions
The KTX extensions include the excellent Kotlin extensions by MOLO17, as well as other convenience functions for composing queries, observing change Flow
s, and creating indexes.
Installation
kotlin {
sourceSets {
commonMain {
dependencies {
implementation("dev.kotbase:couchbase-lite-ktx:3.0.15-1.0.1")
}
}
}
}
Usage
QueryBuilder extensions
The syntax for building a query is more straight-forward thanks to Kotlin's infix
function support.
select(all()) from database where { "type" equalTo "user" }
Or just a bunch of fields:
select("name", "surname") from database where { "type" equalTo "user" }
Or if you also want the document ID:
select(Meta.id, all()) from database where { "type" equalTo "user" }
select(Meta.id, "name", "surname") from database where { "type" equalTo "user" }
You can even do more powerful querying:
select("name", "type")
.from(database)
.where {
("type" equalTo "user" and "name" equalTo "Damian") or
("type" equalTo "pet" and "name" like "Kitt")
}
.orderBy { "name".ascending() }
.limit(10)
There are also convenience extensions for performing SELECT COUNT(*)
queries:
val query = selectCount() from database where { "type" equalTo "user" }
val count = query.execute().countResult()
Document builder DSL
For creating a MutableDocument
ready to be saved, you can use a Kotlin builder DSL:
val document = MutableDocument {
"name" to "Damian"
"surname" to "Giusti"
"age" to 24
"pets" to listOf("Kitty", "Kitten", "Kitto")
"type" to "user"
}
database.save(document)
Flow support
Supplementing the Flow
APIs from Couchbase Lite Android KTX present in the base couchbase-lite modules, Kotbase KTX adds some additional useful Flow
APIs.
Query Flow
Query.asFlow()
builds on top of Query.queryChangeFlow()
to emit non-null ResultSet
s and throw any QueryChange
errors.
select(all())
.from(database)
.where { "type" equalTo "user" }
.asFlow()
.collect { value: ResultSet ->
// consume ResultSet
}
Document Flow
Unlike Database.documentChangeFlow()
, which only emits DocumentChange
s, Database.documentFlow()
handles the common use case of getting the initial document state and observing changes from the database, enabling reactive UI patterns.
database.documentFlow("userProfile")
.collect { doc: Document? ->
// consume Document
}
ResultSet model mapping
Map delegation
Thanks to Map
delegation, mapping a ResultSet
to a Kotlin class has never been so easy.
The library provides the ResultSet.toObjects()
and Query.asObjectsFlow()
extensions for helping to map results given a factory lambda.
Such factory lambdas accept a Map<String, Any?>
and return an instance of a certain type. Those requirements fit perfectly with a Map
-delegated class.
class User(map: Map<String, Any?>) {
val name: String by map
val surname: String by map
val age: Int by map
}
val users: List<User> = query.execute().toObjects(::User)
val usersFlow: Flow<List<User>> = query.asObjectsFlow(::User)
JSON deserialization
Kotbase KTX also provides extensions for mapping documents from a JSON string to Kotlin class. This works well together with a serialization library, like kotlinx-serialization, to decode the JSON string to a Kotlin object.
@Serializable
class User(
val name: String,
val surname: String,
val age: Int
)
val users: List<User> = query.execute().toObjects { json: String ->
Json.decodeFromString<User>(json)
}
val usersFlow: Flow<List<User>> = query.asObjectsFlow { json: String ->
Json.decodeFromString<User>(json)
}
Index creation
Kotbase KTX provides concise top-level functions for index creation:
database.createIndex("typeNameIndex", valueIndex("type", "name"))
database.createIndex("overviewFTSIndex", fullTextIndex("overview"))
Replicator extensions
For the Android platform, you can bind the Replicator
start()
and stop()
methods to be performed automatically when your Lifecycle
-enabled component gets resumed or paused.
// Binds the Replicator to the Application lifecycle.
replicator.bindToLifecycle(ProcessLifecycleOwner.get().lifecycle)
// Binds the Replicator to the Activity/Fragment lifecycle.
// inside an Activity or Fragment...
override fun onCreate(savedInstanceState: Bundle?) {
replicator.bindToLifecycle(lifecycle)
}
That's it! The Replicator
will be automatically started when your component passes the ON_RESUME
state, and it will be stopped when the component passes the ON_PAUSED
state. As you may imagine, no further action will be made after the ON_DESTROY
state.
Couchbase Lite Community Edition – Kotlin Extensions
The KTX extensions include the excellent Kotlin extensions by MOLO17, as well as other convenience functions for composing queries, observing change Flow
s, and creating indexes.
Installation
kotlin {
sourceSets {
commonMain {
dependencies {
implementation("dev.kotbase:couchbase-lite-ktx:3.0.15-1.0.1")
}
}
}
}
Usage
QueryBuilder extensions
The syntax for building a query is more straight-forward thanks to Kotlin's infix
function support.
select(all()) from database where { "type" equalTo "user" }
Or just a bunch of fields:
select("name", "surname") from database where { "type" equalTo "user" }
Or if you also want the document ID:
select(Meta.id, all()) from database where { "type" equalTo "user" }
select(Meta.id, "name", "surname") from database where { "type" equalTo "user" }
You can even do more powerful querying:
select("name", "type")
.from(database)
.where {
("type" equalTo "user" and "name" equalTo "Damian") or
("type" equalTo "pet" and "name" like "Kitt")
}
.orderBy { "name".ascending() }
.limit(10)
There are also convenience extensions for performing SELECT COUNT(*)
queries:
val query = selectCount() from database where { "type" equalTo "user" }
val count = query.execute().countResult()
Document builder DSL
For creating a MutableDocument
ready to be saved, you can use a Kotlin builder DSL:
val document = MutableDocument {
"name" to "Damian"
"surname" to "Giusti"
"age" to 24
"pets" to listOf("Kitty", "Kitten", "Kitto")
"type" to "user"
}
database.save(document)
Flow support
Supplementing the Flow
APIs from Couchbase Lite Android KTX present in the base couchbase-lite modules, Kotbase KTX adds some additional useful Flow
APIs.
Query Flow
Query.asFlow()
builds on top of Query.queryChangeFlow()
to emit non-null ResultSet
s and throw any QueryChange
errors.
select(all())
.from(database)
.where { "type" equalTo "user" }
.asFlow()
.collect { value: ResultSet ->
// consume ResultSet
}
Document Flow
Unlike Database.documentChangeFlow()
, which only emits DocumentChange
s, Database.documentFlow()
handles the common use case of getting the initial document state and observing changes from the database, enabling reactive UI patterns.
database.documentFlow("userProfile")
.collect { doc: Document? ->
// consume Document
}
ResultSet model mapping
Map delegation
Thanks to Map
delegation, mapping a ResultSet
to a Kotlin class has never been so easy.
The library provides the ResultSet.toObjects()
and Query.asObjectsFlow()
extensions for helping to map results given a factory lambda.
Such factory lambdas accept a Map<String, Any?>
and return an instance of a certain type. Those requirements fit perfectly with a Map
-delegated class.
class User(map: Map<String, Any?>) {
val name: String by map
val surname: String by map
val age: Int by map
}
val users: List<User> = query.execute().toObjects(::User)
val usersFlow: Flow<List<User>> = query.asObjectsFlow(::User)
JSON deserialization
Kotbase KTX also provides extensions for mapping documents from a JSON string to Kotlin class. This works well together with a serialization library, like kotlinx-serialization, to decode the JSON string to a Kotlin object.
@Serializable
class User(
val name: String,
val surname: String,
val age: Int
)
val users: List<User> = query.execute().toObjects { json: String ->
Json.decodeFromString<User>(json)
}
val usersFlow: Flow<List<User>> = query.asObjectsFlow { json: String ->
Json.decodeFromString<User>(json)
}
Index creation
Kotbase KTX provides concise top-level functions for index creation:
database.createIndex("typeNameIndex", valueIndex("type", "name"))
database.createIndex("overviewFTSIndex", fullTextIndex("overview"))
Replicator extensions
For the Android platform, you can bind the Replicator
start()
and stop()
methods to be performed automatically when your Lifecycle
-enabled component gets resumed or paused.
// Binds the Replicator to the Application lifecycle.
replicator.bindToLifecycle(ProcessLifecycleOwner.get().lifecycle)
// Binds the Replicator to the Activity/Fragment lifecycle.
// inside an Activity or Fragment...
override fun onCreate(savedInstanceState: Bundle?) {
replicator.bindToLifecycle(lifecycle)
}
That's it! The Replicator
will be automatically started when your component passes the ON_RESUME
state, and it will be stopped when the component passes the ON_PAUSED
state. As you may imagine, no further action will be made after the ON_DESTROY
state.