Skip to content

Instantly share code, notes, and snippets.

@mhewedy
Created June 26, 2025 13:13
Show Gist options
  • Save mhewedy/44a3c60ed1a7c58ffd8866a271444914 to your computer and use it in GitHub Desktop.
Save mhewedy/44a3c60ed1a7c58ffd8866a271444914 to your computer and use it in GitHub Desktop.
package com.example.mongodb.expressions
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import java.time.LocalDate
import java.time.LocalDateTime
/**
* MongoDB Expression Query Builder for Kotlin Multiplatform
*
* This builder creates MongoDB-style JSON queries compatible with
* spring-data-jpa-mongodb-expressions library.
*
* Usage example:
* ```kotlin
* val query = mongoQuery {
* or {
* "lastName" eq "ibrahim"
* and {
* "firstName" eq "mostafa"
* "birthDate" gt "1990-01-01"
* }
* }
* }
*
* // Converts to: {"$or": [{"lastName": "ibrahim"}, {"$and": [{"firstName": "mostafa"}, {"birthDate": {"$gt": "1990-01-01"}}]}]}
* ```
*/
@Serializable
data class MongoExpression(
val expression: JsonElement
) {
fun toJsonString(): String = Json.encodeToString(JsonElement.serializer(), expression)
fun toMap(): Map<String, Any?> = expression.jsonObject.toMap().mapValues { (_, value) ->
when (value) {
is JsonPrimitive -> value.contentOrNull ?: value.boolean
is JsonObject -> value.jsonObject.toMap()
is JsonArray -> value.jsonArray.map { it.toString() }
}
}
}
class MongoQueryBuilder {
private val conditions = mutableListOf<JsonElement>()
// Equality operators
infix fun String.eq(value: Any?): JsonElement {
val jsonValue = when (value) {
null -> JsonNull
is String -> JsonPrimitive(value)
is Number -> JsonPrimitive(value)
is Boolean -> JsonPrimitive(value)
is LocalDate -> JsonPrimitive(value.toString())
is LocalDateTime -> JsonPrimitive(value.toString())
else -> JsonPrimitive(value.toString())
}
return buildJsonObject {
put(this@eq, jsonValue)
}.also { conditions.add(it) }
}
infix fun String.ne(value: Any?): JsonElement {
return buildJsonObject {
put(this@ne, buildJsonObject {
put("\$ne", JsonPrimitive(value.toString()))
})
}.also { conditions.add(it) }
}
infix fun String.ieq(value: Any?): JsonElement {
return buildJsonObject {
put(this@ieq, buildJsonObject {
put("\$ne", JsonPrimitive(value.toString()))
})
}.also { conditions.add(it) }
}
// Comparison operators
infix fun String.gt(value: Any?): JsonElement {
return buildJsonObject {
put(this@gt, buildJsonObject {
put("\$gt", JsonPrimitive(value.toString()))
})
}.also { conditions.add(it) }
}
infix fun String.gte(value: Any?): JsonElement {
return buildJsonObject {
put(this@gte, buildJsonObject {
put("\$gte", JsonPrimitive(value.toString()))
})
}.also { conditions.add(it) }
}
infix fun String.lt(value: Any?): JsonElement {
return buildJsonObject {
put(this@lt, buildJsonObject {
put("\$lt", JsonPrimitive(value.toString()))
})
}.also { conditions.add(it) }
}
infix fun String.lte(value: Any?): JsonElement {
return buildJsonObject {
put(this@lte, buildJsonObject {
put("\$lte", JsonPrimitive(value.toString()))
})
}.also { conditions.add(it) }
}
// Array operators
infix fun String.`in`(values: List<Any?>): JsonElement {
return buildJsonObject {
put(this@`in`, buildJsonObject {
put("\$in", JsonArray(values.map {
when (it) {
null -> JsonNull
is String -> JsonPrimitive(it)
is Number -> JsonPrimitive(it)
is Boolean -> JsonPrimitive(it)
else -> JsonPrimitive(it.toString())
}
}))
})
}.also { conditions.add(it) }
}
infix fun String.nin(values: List<Any?>): JsonElement {
return buildJsonObject {
put(this@nin, buildJsonObject {
put("\$nin", JsonArray(values.map {
when (it) {
null -> JsonNull
is String -> JsonPrimitive(it)
is Number -> JsonPrimitive(it)
is Boolean -> JsonPrimitive(it)
else -> JsonPrimitive(it.toString())
}
}))
})
}.also { conditions.add(it) }
}
// Pattern matching
infix fun String.start(pattern: String): JsonElement {
return buildJsonObject {
put(this@start, buildJsonObject {
put("\$start", JsonPrimitive(pattern))
})
}.also { conditions.add(it) }
}
infix fun String.istart(pattern: String): JsonElement {
return buildJsonObject {
put(this@istart, buildJsonObject {
put("\$istart", JsonPrimitive(pattern))
})
}.also { conditions.add(it) }
}
infix fun String.end(pattern: String): JsonElement {
return buildJsonObject {
put(this@end, buildJsonObject {
put("\$end", JsonPrimitive(pattern))
})
}.also { conditions.add(it) }
}
infix fun String.iend(pattern: String): JsonElement {
return buildJsonObject {
put(this@iend, buildJsonObject {
put("\$iend", JsonPrimitive(pattern))
})
}.also { conditions.add(it) }
}
infix fun String.contains(pattern: String): JsonElement {
return buildJsonObject {
put(this@contains, buildJsonObject {
put("\$contains", JsonPrimitive(pattern))
})
}.also { conditions.add(it) }
}
infix fun String.icontains(pattern: String): JsonElement {
return buildJsonObject {
put(this@icontains, buildJsonObject {
put("\$icontains", JsonPrimitive(pattern))
})
}.also { conditions.add(it) }
}
// Logical operators
fun and(block: MongoQueryBuilder.() -> Unit): JsonElement {
val builder = MongoQueryBuilder()
builder.block()
return buildJsonObject {
put("\$and", JsonArray(builder.conditions))
}.also { conditions.add(it) }
}
fun or(block: MongoQueryBuilder.() -> Unit): JsonElement {
val builder = MongoQueryBuilder()
builder.block()
return buildJsonObject {
put("\$or", JsonArray(builder.conditions))
}.also { conditions.add(it) }
}
// Build the final expression
internal fun build(): JsonElement {
return when (conditions.size) {
0 -> buildJsonObject { }
1 -> conditions.first()
else -> buildJsonObject {
put("\$and", JsonArray(conditions))
}
}
}
}
// DSL function to create MongoDB expressions
fun mongoQuery(builder: MongoQueryBuilder.() -> Unit): MongoExpression {
val queryBuilder = MongoQueryBuilder()
queryBuilder.builder()
return MongoExpression(queryBuilder.build())
}
fun main() {
println(mongoQuery {
"firstName" eq "John"
"age" gt 25
}.toJsonString())
println(mongoQuery {
or {
"lastName" eq "ibrahim"
and {
"firstName" icontains "mostafa"
"birthDate" eq "1990-01-01"
}
}
}.toJsonString())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment