Create an index on an empty collection

Is it supported to create an index on a non-existent brand-new collection in Couchbase Lite? I am using com.couchbase.lite:couchbase-lite-android-ktx:4.0.2 with the following Kotlin:

    private val collection: Collection by lazy {
        database.createCollection("features").also {
            it.createIndex("syncTopicsIndex", ValueIndexConfigurationFactory.newConfig("_syncTopics"))
        }
    }

But if the collection doesn’t already exist, it errors out with:

CouchbaseLiteException{CouchbaseLite.SQLite, 1: no such table: main.kv_.features -- CREATE INDEX syncTopicsIndex ON "kv_.features" (fl_value(body, '_syncTopics')) [3, 1]
	at com.couchbase.lite.Collection.createIndexInternal(Collection.java:789)
	at com.couchbase.lite.Collection.createIndex(Collection.java:477)

Eventually, if I restart the app enough, the empty collection gets created and it starts working. Is this expected? Should I create some dummy value to force the collection to appear first? Or is there some expected wait time, so I should createCollection and make the index later?

Do you have advice on managing indexes, if putting them near the collection like this isn’t correct?

This is interesting. This might be an overlooked case. When you “create” a collection, nothing actually happens until you save some data into it. So I’d guess that writing a value into the collection first would resolve this situation. I’ll file an issue regarding this to see if this can be optimized though.

Filed CBL-7764 for this version

1 Like

I have tested the following on iOS and it works.

let collection = try db.createCollection(name: "features")
let config = ValueIndexConfiguration(["_syncTopics"])
try collection.createIndex(withName: "syncTopicsIndex", config: config)
XCTAssertEqual(try collection.indexes(), ["syncTopicsIndex"])

BTW, on another topic, I’m not sure if the document’s property prefixed with ‘_’ is allowed.

That’s a good point about the prefix, I didn’t think to check at first. But it seems like it’s allowed for local-only documents (Documents | Couchbase Docs), which is what I’m attempting here.

I tried this and it works

val database : Database = Database("testing")
private val collection: Collection by lazy {
    database.createCollection("features").also {
        it.createIndex("syncTopicsIndex", ValueIndexConfigurationFactory.newConfig("_syncTopics"))
    }
}

That setup is expected to work: the collection is initialized lazily, so the collection creation and index creation only happen the first time collection is accessed. In local testing this behaves as expected and works correctly.

Are there any logs or additional details you can share

implementation 'com.couchbase.lite:couchbase-lite-android-ktx:4.0.2'
CouchbaseLiteException{CouchbaseLite.SQLite, 1: no such table: main.kv_.companies -- CREATE INDEX syncTopicsIndex ON "kv_.companies" (fl_value(body, '_syncTopics')) [3, 1]
	at com.couchbase.lite.Collection.createIndexInternal(Collection.java:789)
	at com.couchbase.lite.Collection.createIndex(Collection.java:477)

<snip>

Caused by: com.couchbase.lite.LiteCoreException: no such table: main.kv_.companies -- CREATE INDEX syncTopicsIndex ON "kv_.companies" (fl_value(body, '_syncTopics')) [3, 1]
	at com.couchbase.lite.internal.core.impl.NativeC4Collection.createValueIndex(Native Method)
	at com.couchbase.lite.internal.core.impl.NativeC4Collection.nCreateValueIndex(NativeC4Collection.java:88)
	at com.couchbase.lite.internal.core.C4Collection.lambda$createValueIndex$9$com-couchbase-lite-internal-core-C4Collection(C4Collection.java:295)
	at com.couchbase.lite.internal.core.C4Collection$$ExternalSyntheticLambda10.accept(D8$$SyntheticClass:0)
	at com.couchbase.lite.internal.core.C4Peer.voidWithPeerOrWarn(C4Peer.java:243)
	at com.couchbase.lite.internal.core.C4Collection.createValueIndex(C4Collection.java:294)
	at com.couchbase.lite.ValueIndexConfiguration.createIndex(ValueIndexConfiguration.java:50)
	at com.couchbase.lite.Collection.createIndexInternal(Collection.java:787)
	... 20 more

I am initializing couchbase like the following:

            CouchbaseLite.init(context)

            val dbDir = File(context.filesDir, "foo")
            if (!dbDir.exists()) {
                throw Exception("Db dir does not exist")
            }

            val config = DatabaseConfiguration().apply {
                directory = dbDir.absolutePath
            }

            database = Database("foo", config)

and lazily creating the collection per my initial post. Some collections already exist and are successfully working The error only happens when I have never touched the collection before. If I reboot the app several times, it eventually succeeds in creating the index, so it’s not reliable.