The next MongoDB driver for Kotlin ktmongo.opensavvy.dev
kotlin mongodb multiplatform
README.md

DSL template #

A template module for the :dsl module.

Do NOT modify the production code of the :dsl module directly. Instead, modify this template module. Tests, however, are written in the :dsl module and not in :dsl-template.

This module is not published.

The problem #

The :dsl module contains the 'meat' of the KtMongo project: the entire operator DSL, including aggregation support.

To be convenient for users, we regularly need to provide multiple overloads of each operator. For example, the type Field<T, V> represents a path to a field:

users.find {
	User::profile / Profile::age eq 20
	// ^^^^^^^^^^^^^^^^^^^^^^^^^
	// Field("profile.age")
}

The Field interface allows complex path generation. However, top-level properties inherently are typed as KProperty1<T, V>, as they are a Kotlin feature. We cannot control that type.

users.find {
	User::name eq "Bob"
	// ^^^^^^^
	// KProperty1<User, String>("name")
}

Therefore, we need an overload:

infix fun <V> KProperty1<T, V>.eq(value: V) {
	this.field eq value
}

where KProperty1<T, V>.field is the conversion function.

All regular operators (filters, updates, projections, indexes…) need both Field and KProperty1 overloads. However, aggregation operators go even further, requiring four overloads:

  • opensavvy.ktmongo.dsl.aggregation.Value<T, V> (the actual type of aggregation expressions)
  • opensavvy.ktmongo.dsl.path.Field<T, V>
  • kotlin.reflect.KProperty1<T, V>
  • T

Note that the last two are not our types, so it isn't possible to create a supertype. T isn't even a known type.

Additionally, KotlinX.Serialization must know the compile-time type of any serialized value. This can only be accessed in an inline function, so all of these require additional inline overloads.

The solution #

Instead of developing the DSL module directly, all production code is written in this template module. On each build, the :dsl module is regenerated by copying this template module, adding all the necessary overloads in the process.

The templating engine is implemented in the dsl-templator Gradle plugin. It is executed by running ./gradlew :dsl:applyTemplate.