In this part of the learning path, you will be working with the "Audit Inventory" demo app that allows users to log in and access the developer screens in order to validate the use of a pre-build database in the demo application that is used to store warehouse and stock item documents. The warehouse and stock item documents will be used in future steps of the learning path when we need to add projects and audits to the database.
In this step of the learning path you will learn the fundamentals of:
While the demo app has a lot of functionality, this step will walk you through:
Learn Couchbase Lite with Kotlin and Jetpack Compose
repository from GitHub.git clone https://github.com/couchbase-examples/android-kotlin-cbl-learning-path.git
No Items found in the database
message on the Projects screen.A reminder that Couchbase Lite is a JSON Document Store. A Document
is a logical collection of named fields and values. The values are any valid JSON types. In addition to the standard JSON types, Couchbase Lite supports Date
and Blob
data types. While it is not required or enforced, it is a recommended practice to include a "type" property that can serve as a namespace for related documents.
The sample app comes bundled with a collection of Document
with a "documentType" property of "warehouse". Each document represents an warehouse location that a team would visit in order to perform an audit of the inventory at that location.
An example of a document would be:
{
"warehouseId":"e1839e0b-57a0-472c-b29d-8d57e256ef32",
"name":"Santa Clara Warehouse",
"address1":"3250 Dr Olcott Street",
"address2":"",
"city":"Santa Clara",
"state":"CA",
"postalCode":"95054",
"salesTax":0.0913,
"latitude":32.3803024,
"longitude":-121.9674197,
"documentType":"warehouse",
"yearToDateBalance":0,
"shippingTo": [
"AZ",
"CA",
"HI",
"NV"
],
}
When a "warehouse" is retreived from the database it is stored within an Data Class of type Warehouse.
data class Warehouse(
val warehouseId: String,
val name: String,
val address1: String,
val address2: String? = "",
val city: String,
val state: String,
val postalCode: String,
val salesTax: Double,
val yearToDateBalance: Double,
val latitude: Double,
val longitude: Double,
val documentType: String,
val shippingTo: List<String>,
)
The sample app comes bundled with a collection of Document
with a "documentType" property of "item". Each document represents an item in stock that a team would count in order to perform an audit of the inventory in the warehouse.
An example of a document would be:
{
"itemId":"00b66fdf-9bdb-451b-bd2a-75bdf0459958",
"name":"Bachensteiner Beard Export",
"price":24.22,
"description":"Tranquil Export with Bachensteiner flavors",
"style": "Imperial Stout",
"documentType":"item"
}
When a "item" is retreived from the database it is stored within an Data Class of type StockItem.
data class StockItem (
var itemId: String = "",
var name: String = "",
var price: Float,
var description: String = "",
var style: String = "",
var documentType: String = "item")
There are several reasons why you may want to bundle your app with a prebuilt database. This would be suited for data that does not change or change that often, so you can avoid the bandwidth and latency involved in fetching/syncing this data from a remote server. This also improves the overall user experience by reducing the start-up time.
In our app, the instance of Couchbase Lite that holds the pre-loaded "warehouse" and "stockItem" data is separate from the Couchbase Lite instance that holds "user", "project", and "audit" data. A separate Couchbase Lite instance is not required. However, in our case, since there can be many users potentially using the app on a given device, it makes more sense to keep it separate. This is to avoid duplication of pre-loaded data for every user and to help speed up the app vs pulling down the warehoue and items when the app is first installed.
The pre-built database will be in the form of a cblite file. It should be in your app project bundle
Note: The cblite folder will be extracted from the zip file.
initializeDatabases()
function. The prebuilt database is common to all users of the app (on the device). So it will be loaded once and shared by all users on the device. Note that the currentUser is required to setup the inventory database for use which holds the user profile documents, as covered in the Key Value step of the learning path.fun initializeDatabases(currentUser: User)
DatabaseConfiguration
object and specify the path where the database would be locatedval dbConfig = DatabaseConfigurationFactory.create(context.filesDir.toString())
If the database is already present at the specified Database location, we simply open the database.
private fun setupWarehouseDatabase(dbConfig: DatabaseConfiguration) {
// create the warehouse database if it doesn't already exist
if (!Database.exists(warehouseDatabaseName, context.filesDir)) {
unzip(startingWarehouseFileName, File(context.filesDir.toString()))
// copy the warehouse database to the project database
// never open the database directly as this will cause issues
// with sync
val warehouseDbFile =
File(
String.format(
"%s/%s",
context.filesDir,
("${startingWarehouseDatabaseName}.cblite2")
)
)
Database.copy(warehouseDbFile, warehouseDatabaseName, dbConfig)
}
warehouseDatabase = Database(warehouseDatabaseName, dbConfig)
}
Note: You MUST copy the pre-built database using the Database.copy function instead of opening it directly or you will run into issues with data syncronization.
Creating indexes for non-FTS based queries is optional. However, to speed up queries, you can create indexes on the properties that you would query against. Indexes can slow down writes, so it's recommended adding indexes as you need them. Indexing is handled eagerly.
In the DatabaseManager.kt file, locate the createTypeIndex(database: Database?)
function.
We create an index on the documentType
property of the documents in the database that is referenced to the function using the databases createIndex function. The createIndex function requires the name of the index along with the expression of what to index.
private fun createTypeIndex(
database: Database?
) {
// create index for document type if it doesn't exist
database?.let {
if (!it.indexes.contains(typeIndexName)) {
it.createIndex(
typeIndexName, IndexBuilder.valueIndex(
ValueIndexItem.expression(
Expression.property(typeAttributeName))
)
)
}
}
}
Note: Since both databases have documents that use a
documentType
property, it was easier to create a function that could be used to create the index for either database. ThetypeIndexName
andtypeAttributeName
variables are defined at the top of the DatabaseManager class.
When a user logs out, we close the pre-built database along with other user-specific databases
In the DatabaseManager.kt file, locate the closeDatabases()
function.
Closing the databases is pretty straightforward
fun closeDatabases() {
try {
inventoryDatabase?.close()
warehouseDatabase?.close()
} catch (e: java.lang.Exception) {
android.util.Log.e(e.message, e.stackTraceToString())
}
}
I/CouchbaseLite/DATABASE: Copying prebuilt database from
/data/data/com.couchbase.kotlin.learningpath/files/startingWarehouses.cblite2/
to /data/user/0/com.couchbase.kotlin.learningpath/files/warehouse.cblite2
Congratulations on completing this step of our learning path!
This step of the learning path walked you through an example of how to use a pre-built Couchbase Lite database. Check out the following links for further documenation and continue on to the next step that covers how to insert documents into the database using Batch operations.
References