update ts-mongodb

This commit is contained in:
tursom 2022-04-02 16:10:51 +08:00
parent 40801d52fb
commit 657f1a31e6
15 changed files with 916 additions and 164 deletions

View File

@ -691,3 +691,9 @@ fun ByteArray.gz(): ByteArray {
fun ByteArray.ungz(): ByteArray {
return GZIPInputStream(ByteArrayInputStream(this)).readBytes()
}
fun StringBuilder(vararg strings: String): StringBuilder {
val builder = kotlin.text.StringBuilder(strings.sumOf { it.length })
builder.append(value = strings)
return builder
}

View File

@ -7,7 +7,11 @@ plugins {
dependencies {
api(project(":"))
implementation(project(":ts-core"))
implementation(project(":ts-core:ts-clone"))
compileOnly("org.mongodb:mongo-java-driver:3.12.10")
compileOnly(group = "org.springframework.data", name = "spring-data-mongodb", version = "3.3.0")
testApi(group = "junit", name = "junit", version = "4.13.2")
testImplementation(group = "org.springframework.data", name = "spring-data-mongodb", version = "3.3.0")
}

View File

@ -29,5 +29,5 @@ interface BsonConverter {
}
}
fun Any.bsonValue(): Any = bsonValue(this)
fun Any.bsonValue(): Any = Companion.bsonValue(this)
}

View File

@ -0,0 +1,54 @@
package cn.tursom.database.mongodb.spring
import org.springframework.data.mongodb.core.aggregation.AggregationExpression
import org.springframework.data.mongodb.core.aggregation.StringOperators
import kotlin.reflect.KProperty
class ConcatBuilder {
companion object : MongoName, BsonConverter {
inline infix operator fun invoke(operator: ConcatBuilder.() -> Unit): StringOperators.Concat {
val setBuilder = ConcatBuilder()
setBuilder.operator()
return when (setBuilder.list.size) {
0 -> StringOperators.Concat.stringValue("")
1 -> getConcat(setBuilder.list[0])
else -> {
val iterator = setBuilder.list.iterator()
var concat = getConcat(iterator.next())
iterator.forEach {
concat = when (it) {
is KProperty<*> -> concat.concatValueOf(it.mongoName)
is FieldReference -> concat.concatValueOf(it.field)
is AggregationExpression -> concat.concatValueOf(it)
else -> concat.concat(it.toString())
}
}
concat
}
}
}
fun getConcat(value: Any?) = when (value) {
is KProperty<*> -> StringOperators.Concat.valueOf(value.mongoName)
is FieldReference -> StringOperators.Concat.valueOf(value.field)
is AggregationExpression -> StringOperators.Concat.valueOf(value)
else -> StringOperators.Concat.stringValue(value.toString())
}
}
data class FieldReference(val field: String)
val list = ArrayList<Any?>()
operator fun Any?.unaryPlus() {
list.add(this)
}
operator fun AggregationExpression.unaryPlus() {
list.add(this)
}
fun field(field: String) {
list.add(FieldReference(field))
}
}

View File

@ -0,0 +1,23 @@
package cn.tursom.database.mongodb.spring
import org.bson.Document
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext
import org.springframework.data.mongodb.core.aggregation.ExposedFields
import org.springframework.data.mongodb.core.aggregation.Field
object DirectAggregationOperationContext : AggregationOperationContext {
override fun getMappedObject(document: Document, type: Class<*>?): Document = document
private class DirectAggregationOperationReference(name: String) : ExposedFields.FieldReference {
private val name = "$$name"
override fun getRaw(): String? = null
override fun getReferenceValue(): Any? = null
override fun toString(): String = name
}
override fun getReference(field: Field): ExposedFields.FieldReference =
DirectAggregationOperationReference(field.name)
override fun getReference(name: String): ExposedFields.FieldReference =
DirectAggregationOperationReference(name)
}

View File

@ -0,0 +1,13 @@
package cn.tursom.database.mongodb.spring
import cn.tursom.core.uncheckedCast
import com.mongodb.client.model.FindOneAndUpdateOptions
import org.bson.conversions.Bson
inline fun FindOneAndUpdateOptions.arrayFilter(builder: QueryBuilder.() -> Unit) {
if (arrayFilters == null) {
arrayFilters(ArrayList())
}
arrayFilters.uncheckedCast<MutableList<Bson>>()
.add(QueryBuilder queryObject builder)
}

View File

@ -1,5 +1,6 @@
package cn.tursom.database.mongodb.spring
import cn.tursom.core.StringBuilder
import cn.tursom.database.mongodb.spring.function.Function1
import cn.tursom.database.mongodb.spring.function.field
import org.springframework.data.annotation.Id
@ -8,9 +9,11 @@ import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaField
@Suppress("MemberVisibilityCanBePrivate")
interface MongoName {
companion object {
const val dollar = '$'
const val dollar = "$"
const val dotDollar = "$"
inline operator fun <T> invoke(builder: Companion.() -> T) = this.builder()
inline val KProperty<*>.mongoName: String
@ -19,7 +22,7 @@ interface MongoName {
fun arrayElement(elementName: String = "") = if (elementName.isBlank()) {
"$"
} else {
"$[$elementName]"
"$.$elementName"
}
fun mongoName(kProperty: KProperty<*>): String {
@ -50,38 +53,103 @@ interface MongoName {
fun <T> mongoName(getter: Function1<*, T>): String = mongoName(getter.field!!)
infix operator fun KProperty<*>.plus(kProperty: KProperty<*>) = "$mongoName.${kProperty.mongoName}"
infix operator fun KProperty<*>.plus(field: String) = "$mongoName.${field}"
infix operator fun String.plus(kProperty: KProperty<*>) = "$this.${kProperty.mongoName}"
infix operator fun String.plus(field: String) = "$this.${field}"
infix operator fun KProperty<*>.plus(kProperty: KProperty<*>) =
StringBuilder(mongoName, ".", kProperty.mongoName)
infix operator fun KProperty<*>.get(kProperty: KProperty<*>) = plus(kProperty)
infix operator fun KProperty<*>.get(field: String) = plus(field)
infix operator fun String.get(kProperty: KProperty<*>) = plus(kProperty)
infix operator fun String.get(field: String) = plus(field)
infix operator fun KProperty<*>.plus(field: String) = StringBuilder(mongoName, ".", field)
infix operator fun KProperty<*>.plus(field: StringBuilder) = StringBuilder(mongoName, ".").append(field)
infix operator fun KProperty<*>.rem(kProperty: KProperty<*>) = plus(kProperty)
infix operator fun KProperty<*>.rem(field: String) = plus(field)
infix operator fun String.rem(kProperty: KProperty<*>) = plus(kProperty)
infix operator fun String.rem(field: String) = plus(field)
infix operator fun String.plus(kProperty: KProperty<*>) = StringBuilder(this, ".", kProperty.mongoName)
infix operator fun String.plus(field: String) = StringBuilder(this, ".", field)
infix operator fun String.plus(field: StringBuilder) = StringBuilder(this, ".").append(field)
infix operator fun StringBuilder.plus(kProperty: KProperty<*>) = append(".", kProperty.mongoName)
infix operator fun StringBuilder.plus(field: String) = append(".", field)
infix operator fun StringBuilder.plus(field: StringBuilder) = append(".", field)
fun KProperty<*>.arrayElement(kProperty: KProperty<*>) =
StringBuilder(mongoName, ".", dollar, ".", kProperty.mongoName)
fun KProperty<*>.arrayElement(field: String) =
StringBuilder(mongoName, ".", dollar, ".", field)
fun KProperty<*>.arrayElement(field: StringBuilder) =
StringBuilder(mongoName, ".", dollar, ".").append(field)
fun String.arrayElement(kProperty: KProperty<*>) =
StringBuilder(this, ".", dollar, ".", kProperty.mongoName)
fun String.arrayElement(field: String) =
StringBuilder(this, ".", dollar, ".", field)
fun String.arrayElement(field: StringBuilder) =
StringBuilder(this, ".", dollar, ".").append(field)
fun StringBuilder.arrayElement(kProperty: KProperty<*>) =
append(".", dollar, ".", kProperty.mongoName)
fun StringBuilder.arrayElement(field: String) =
append(".", dollar, ".", field)
fun StringBuilder.arrayElement(field: StringBuilder) =
append(".", dollar, ".", field)
infix operator fun KProperty<*>.get(kProperty: KProperty<*>) = arrayElement(kProperty)
infix operator fun KProperty<*>.get(field: String) = arrayElement(field)
infix operator fun KProperty<*>.get(field: StringBuilder) = arrayElement(field)
infix operator fun String.get(kProperty: KProperty<*>) = arrayElement(kProperty)
infix operator fun String.get(field: String) = arrayElement(field)
infix operator fun String.get(field: StringBuilder) = arrayElement(field)
infix operator fun StringBuilder.get(kProperty: KProperty<*>) = arrayElement(kProperty)
infix operator fun StringBuilder.get(field: String) = arrayElement(field)
infix operator fun StringBuilder.get(field: StringBuilder) = arrayElement(field)
infix operator fun KProperty<*>.rem(kProperty: KProperty<*>) = this plus kProperty
infix operator fun KProperty<*>.rem(field: String) = this plus field
infix operator fun String.rem(kProperty: KProperty<*>) = this plus kProperty
infix operator fun String.rem(field: String) = this plus field
infix operator fun StringBuilder.rem(kProperty: KProperty<*>) = this plus kProperty
infix operator fun StringBuilder.rem(field: String) = this plus field
}
val dollar get() = Companion.dollar
val dotDollar get() = Companion.dotDollar
val KProperty<*>.mongoName: String
get() = mongoName(this)
val arrayElements: String get() = "$[]"
fun arrayElement(elementName: String = "") = Companion.arrayElement(elementName)
fun arrayElement(kProperty: KProperty<*>) = Companion.arrayElement(kProperty.mongoName)
fun mongoName(kProperty: KProperty<*>): String = Companion.mongoName(kProperty)
fun mongoName(field: java.lang.reflect.Field): String = Companion.mongoName(field)
fun <T> mongoName(getter: Function1<*, T>): String = Companion.mongoName(getter)
infix operator fun KProperty<*>.get(kProperty: KProperty<*>) = Companion { this@get plus kProperty }
infix operator fun KProperty<*>.get(field: String) = Companion { this@get plus field }
infix operator fun String.get(kProperty: KProperty<*>) = Companion { this@get plus kProperty }
infix operator fun String.get(field: String) = Companion { this@get plus field }
val KProperty<*>.arrayFilter: StringBuilder
get() = Companion {
StringBuilder(mongoName, ".$[", mongoName, "]")
}
infix operator fun KProperty<*>.rem(kProperty: KProperty<*>) = Companion { this@rem plus kProperty }
infix operator fun KProperty<*>.rem(field: String) = Companion { this@rem plus field }
infix operator fun String.rem(kProperty: KProperty<*>) = Companion { this@rem plus kProperty }
infix operator fun String.rem(field: String) = Companion { this@rem plus field }
fun KProperty<*>.arrayFilter(field: String): StringBuilder = Companion {
StringBuilder(mongoName, ".$[", field, "]")
}
infix operator fun KProperty<*>.get(kProperty: KProperty<*>) = Companion { this@get get kProperty }
infix operator fun KProperty<*>.get(field: String) = Companion { this@get get field }
infix operator fun KProperty<*>.get(field: StringBuilder) = Companion { this@get get field }
infix operator fun String.get(kProperty: KProperty<*>) = Companion { this@get get kProperty }
infix operator fun String.get(field: String) = Companion { this@get get field }
infix operator fun String.get(field: StringBuilder) = Companion { this@get get field }
infix operator fun StringBuilder.get(kProperty: KProperty<*>) = Companion { this@get get kProperty }
infix operator fun StringBuilder.get(field: String) = Companion { this@get get field }
infix operator fun StringBuilder.get(field: StringBuilder) = Companion { this@get get field }
infix operator fun KProperty<*>.rem(kProperty: KProperty<*>) = Companion { this@rem % kProperty }
infix operator fun KProperty<*>.rem(field: String) = Companion { this@rem % field }
infix operator fun KProperty<*>.rem(field: StringBuilder) = Companion { this@rem % field.toString() }
infix operator fun String.rem(kProperty: KProperty<*>) = Companion { this@rem % kProperty }
infix operator fun String.rem(field: String) = Companion { this@rem % field }
infix operator fun String.rem(field: StringBuilder) = Companion { this@rem % field.toString() }
infix operator fun StringBuilder.rem(kProperty: KProperty<*>) = Companion { this@rem % kProperty }
infix operator fun StringBuilder.rem(field: String) = Companion { this@rem % field }
infix operator fun StringBuilder.rem(field: StringBuilder) = Companion { this@rem % field.toString() }
}

View File

@ -17,7 +17,7 @@ object MongoUtil {
else -> null
}
} ?: clazz.simpleName.toCharArray().let {
it[0] = it[0].toLowerCase()
it[0] = it[0].lowercaseChar()
it
}.concatToString()
@ -30,9 +30,9 @@ object MongoUtil {
entity.javaClass.declaredFields.filter {
it.isAccessible = true
!it.isStatic()
&& !it.isTransient()
//&& it.getAnnotation(Ignore::class.java) == null
&& (it.type != Lazy::class.java || it.get(entity).uncheckedCast<Lazy<*>>().isInitialized())
&& !it.isTransient()
//&& it.getAnnotation(Ignore::class.java) == null
&& (it.type != Lazy::class.java || it.get(entity).uncheckedCast<Lazy<*>>().isInitialized())
}.forEach {
injectValue(bson, it.get(entity) ?: return@forEach, it)
}

View File

@ -10,7 +10,7 @@ import kotlin.reflect.KProperty
@Suppress("unused", "MemberVisibilityCanBePrivate")
object ProjectBuilder : MongoName, BsonConverter {
inline infix operator fun invoke(
operator: ProjectBuilder.() -> ProjectionOperation
operator: ProjectBuilder.() -> ProjectionOperation,
): ProjectionOperation = this.operator()
fun project(): ProjectionOperation = Aggregation.project()
@ -19,6 +19,7 @@ object ProjectBuilder : MongoName, BsonConverter {
Aggregation.project(*fields.map { it.mongoName }.toTypedArray())
infix fun ProjectionOperation.and(name: String): ProjectionOperationBuilder = and(name)
infix fun ProjectionOperation.and(name: StringBuilder): ProjectionOperationBuilder = and(name.toString())
infix fun ProjectionOperation.and(name: KProperty<*>): ProjectionOperationBuilder = and(name.mongoName)
infix fun ProjectionOperation.andExclude(fields: KProperty<*>): ProjectionOperation = andExclude(listOf(fields))
@ -28,9 +29,15 @@ object ProjectBuilder : MongoName, BsonConverter {
infix fun ProjectionOperation.andInclude(fields: KProperty<*>): ProjectionOperation = andInclude(listOf(fields))
fun ProjectionOperation.andInclude(vararg fields: KProperty<*>): ProjectionOperation = andInclude(fields.asList())
@JvmName("andIncludeKProperty")
infix fun ProjectionOperation.andInclude(fields: Collection<KProperty<*>>): ProjectionOperation =
andInclude(*fields.map { it.mongoName }.toTypedArray())
@JvmName("andIncludeString")
infix fun ProjectionOperation.andInclude(fields: Collection<String>): ProjectionOperation =
andInclude(*fields.toTypedArray())
infix fun ProjectionOperationBuilder.nested(fields: Fields): ProjectionOperation = nested(fields)
infix fun ProjectionOperationBuilder.`as`(alias: String): ProjectionOperation = `as`(alias)
@ -106,7 +113,9 @@ object ProjectBuilder : MongoName, BsonConverter {
infix fun ProjectionOperationBuilder.log(base: Number): ProjectionOperationBuilder = log(base)
infix fun ProjectionOperationBuilder.pow(exponentFieldRef: String): ProjectionOperationBuilder = pow(exponentFieldRef)
infix fun ProjectionOperationBuilder.pow(exponentFieldRef: String): ProjectionOperationBuilder =
pow(exponentFieldRef)
infix fun ProjectionOperationBuilder.pow(exponentFieldRef: KProperty<*>): ProjectionOperationBuilder =
pow(exponentFieldRef.mongoName)
@ -204,7 +213,9 @@ object ProjectBuilder : MongoName, BsonConverter {
infix fun String.log(base: Number): ProjectionOperationBuilder = project() and this log (base)
infix fun String.pow(exponentFieldRef: String): ProjectionOperationBuilder = project() and this pow (exponentFieldRef)
infix fun String.pow(exponentFieldRef: String): ProjectionOperationBuilder =
project() and this pow (exponentFieldRef)
infix fun String.pow(exponentFieldRef: KProperty<*>): ProjectionOperationBuilder =
project() and this pow (exponentFieldRef.mongoName)
@ -216,7 +227,6 @@ object ProjectBuilder : MongoName, BsonConverter {
infix fun String.strCaseCmpValueOf(fieldRef: KProperty<*>): ProjectionOperationBuilder =
project() and this strCaseCmpValueOf (fieldRef.mongoName)
infix fun KProperty<*>.nested(fields: Fields): ProjectionOperation = project() and this nested (fields)
infix fun KProperty<*>.`as`(alias: String): ProjectionOperation = project() and this `as` (alias)
infix fun KProperty<*>.alias(alias: String): ProjectionOperation = project() and this `as` (alias)
@ -235,7 +245,9 @@ object ProjectBuilder : MongoName, BsonConverter {
infix operator fun KProperty<*>.plus(fieldReference: KProperty<*>): ProjectionOperationBuilder =
project() and this plus (fieldReference.mongoName)
infix operator fun KProperty<*>.minus(number: Number): ProjectionOperationBuilder = project() and this minus (number)
infix operator fun KProperty<*>.minus(number: Number): ProjectionOperationBuilder =
project() and this minus (number)
infix operator fun KProperty<*>.minus(fieldReference: String): ProjectionOperationBuilder =
project() and this minus (fieldReference)
@ -311,6 +323,102 @@ object ProjectBuilder : MongoName, BsonConverter {
infix fun KProperty<*>.strCaseCmpValueOf(fieldRef: KProperty<*>): ProjectionOperationBuilder =
project() and this strCaseCmpValueOf (fieldRef.mongoName)
infix fun StringBuilder.nested(fields: Fields): ProjectionOperation = project() and this nested fields
infix fun StringBuilder.`as`(alias: String): ProjectionOperation = project() and this `as` alias
infix fun StringBuilder.alias(alias: String): ProjectionOperation = project() and this `as` alias
infix fun StringBuilder.`as`(alias: KProperty<*>): ProjectionOperation = project() and this `as` alias.mongoName
infix fun StringBuilder.alias(alias: KProperty<*>): ProjectionOperation = project() and this `as` alias.mongoName
infix fun StringBuilder.applyCondition(cond: ConditionalOperators.Cond): ProjectionOperation =
project() and this applyCondition cond
infix fun StringBuilder.applyCondition(ifNull: ConditionalOperators.IfNull): ProjectionOperation =
project() and this applyCondition ifNull
infix operator fun StringBuilder.plus(number: Number): ProjectionOperationBuilder = project() and this plus number
infix operator fun StringBuilder.plus(fieldReference: String): ProjectionOperationBuilder =
project() and this plus fieldReference
infix operator fun StringBuilder.plus(fieldReference: KProperty<*>): ProjectionOperationBuilder =
project() and this plus fieldReference.mongoName
infix operator fun StringBuilder.minus(number: Number): ProjectionOperationBuilder = project() and this minus number
infix operator fun StringBuilder.minus(fieldReference: String): ProjectionOperationBuilder =
project() and this minus fieldReference
infix operator fun StringBuilder.minus(fieldReference: KProperty<*>): ProjectionOperationBuilder =
project() and this minus fieldReference.mongoName
infix fun StringBuilder.multiply(number: Number): ProjectionOperationBuilder = project() and this multiply number
infix fun StringBuilder.multiply(fieldReference: String): ProjectionOperationBuilder =
project() and this multiply fieldReference
infix fun StringBuilder.multiply(fieldReference: KProperty<*>): ProjectionOperationBuilder =
project() and this multiply fieldReference.mongoName
infix operator fun StringBuilder.times(number: Number): ProjectionOperationBuilder =
project() and this multiply number
infix operator fun StringBuilder.times(fieldReference: String): ProjectionOperationBuilder =
project() and this multiply fieldReference
infix operator fun StringBuilder.times(fieldReference: KProperty<*>): ProjectionOperationBuilder =
project() and this multiply fieldReference.mongoName
infix fun StringBuilder.divide(number: Number): ProjectionOperationBuilder = project() and this divide number
infix fun StringBuilder.divide(fieldReference: String): ProjectionOperationBuilder =
project() and this divide fieldReference
infix fun StringBuilder.divide(fieldReference: KProperty<*>): ProjectionOperationBuilder =
project() and this divide fieldReference.mongoName
infix operator fun StringBuilder.div(number: Number): ProjectionOperationBuilder = project() and this divide number
infix operator fun StringBuilder.div(fieldReference: String): ProjectionOperationBuilder =
project() and this divide fieldReference
infix operator fun StringBuilder.div(fieldReference: KProperty<*>): ProjectionOperationBuilder =
project() and this divide fieldReference.mongoName
infix fun StringBuilder.mod(number: Number): ProjectionOperationBuilder = project() and this mod number
infix fun StringBuilder.mod(fieldReference: String): ProjectionOperationBuilder =
project() and this mod fieldReference
infix fun StringBuilder.mod(fieldReference: KProperty<*>): ProjectionOperationBuilder =
project() and this mod fieldReference.mongoName
infix fun StringBuilder.cmp(compareValue: Any): ProjectionOperationBuilder = project() and this cmp compareValue
infix fun StringBuilder.eq(compareValue: Any): ProjectionOperationBuilder = project() and this eq compareValue
infix fun StringBuilder.gt(compareValue: Any): ProjectionOperationBuilder = project() and this gt compareValue
infix fun StringBuilder.gte(compareValue: Any): ProjectionOperationBuilder = project() and this gte compareValue
infix fun StringBuilder.lt(compareValue: Any): ProjectionOperationBuilder = project() and this lt compareValue
infix fun StringBuilder.lte(compareValue: Any): ProjectionOperationBuilder = project() and this lte compareValue
infix fun StringBuilder.ne(compareValue: Any): ProjectionOperationBuilder = project() and this ne compareValue
infix fun StringBuilder.slice(count: Int): ProjectionOperationBuilder = project() and this slice count
fun StringBuilder.slice(count: Int, offset: Int): ProjectionOperationBuilder =
(project() and this).slice(count, offset)
infix fun StringBuilder.log(baseFieldRef: String): ProjectionOperationBuilder = project() and this log baseFieldRef
infix fun StringBuilder.log(baseFieldRef: KProperty<*>): ProjectionOperationBuilder =
project() and this log baseFieldRef.mongoName
infix fun StringBuilder.log(base: Number): ProjectionOperationBuilder = project() and this log base
infix fun StringBuilder.pow(exponentFieldRef: String): ProjectionOperationBuilder =
project() and this pow exponentFieldRef
infix fun StringBuilder.pow(exponentFieldRef: KProperty<*>): ProjectionOperationBuilder =
project() and this pow exponentFieldRef.mongoName
infix fun StringBuilder.pow(exponent: Number): ProjectionOperationBuilder = project() and this pow exponent
infix fun StringBuilder.strCaseCmpValueOf(fieldRef: String): ProjectionOperationBuilder =
project() and this strCaseCmpValueOf fieldRef
infix fun StringBuilder.strCaseCmpValueOf(fieldRef: KProperty<*>): ProjectionOperationBuilder =
project() and this strCaseCmpValueOf fieldRef.mongoName
@JvmName("concatArrays_")
fun String.concatArrays(fields: Collection<String>): ProjectionOperationBuilder =
(project() and this).concatArrays(*fields.toTypedArray())

View File

@ -1,6 +1,5 @@
package cn.tursom.database.mongodb.spring
import cn.tursom.core.uncheckedCast
import org.bson.BsonRegularExpression
import org.bson.Document
import org.bson.types.ObjectId
@ -18,46 +17,27 @@ import java.util.*
import java.util.regex.Pattern
import kotlin.reflect.KProperty
@Suppress("MemberVisibilityCanBePrivate", "unused")
@Suppress("MemberVisibilityCanBePrivate", "unused", "HasPlatformType")
class QueryBuilder : MongoName {
companion object {
const val where = "\$where"
private val criteriaField: java.lang.reflect.Field? = try {
Criteria::class.java.getDeclaredField("criteria")
} catch (e: Exception) {
null
}
private val isValueField: java.lang.reflect.Field? = try {
Criteria::class.java.getDeclaredField("isValue")
} catch (e: Exception) {
null
}
private val criteriaChainField: java.lang.reflect.Field? = try {
Criteria::class.java.getDeclaredField("criteriaChain")
} catch (e: Exception) {
null
}
init {
criteriaField?.isAccessible = true
isValueField?.isAccessible = true
criteriaChainField?.isAccessible = true
inline infix operator fun invoke(operator: QueryBuilder.() -> Unit): Query = QueryBuilder()(operator)
inline infix fun queryObject(operator: QueryBuilder.() -> Unit): Document = this(operator).queryObject
inline infix fun fieldsObject(operator: QueryBuilder.() -> Unit): Document = this(operator).fieldsObject
inline infix fun sortObject(operator: QueryBuilder.() -> Unit): Document = this(operator).sortObject
inline infix fun criteria(operator: QueryBuilder.() -> Unit): Criteria = QueryBuilder().apply(operator).and
}
inline infix operator fun invoke(operator: QueryBuilder.() -> Unit): Query {
this.operator()
val query = Query(and)
queryHandler.forEach { handler ->
query.handler()
}
inline infix operator fun invoke(operator: QueryBuilder.() -> Unit): Query = QueryBuilder().let { builder ->
builder.operator()
val query = Query(builder.and)
builder.queryHandler.forEach { handler ->
query.handler()
}
query
}
infix fun queryObject(operator: QueryBuilder.() -> Unit): Document = this(operator).queryObject
infix fun fieldsObject(operator: QueryBuilder.() -> Unit): Document = this(operator).fieldsObject
infix fun sortObject(operator: QueryBuilder.() -> Unit): Document = this(operator).sortObject
infix fun criteria(operator: QueryBuilder.() -> Unit): Criteria = QueryBuilder().apply(operator).and
return query
}
val queryHandler = LinkedList<Query.() -> Unit>()
@ -65,33 +45,10 @@ class QueryBuilder : MongoName {
private val fieldSet = HashSet<String>()
val and: Criteria
get() = when (criteriaList.size) {
0 -> Criteria()
1 -> criteriaList.first()
else -> if (uniqueField && criteriaField != null && isValueField != null) {
var base = Criteria()
criteriaList.forEach {
if (it.key != null) {
base = base.and(it.key!!)
criteriaField.set(base, criteriaField.get(it))
isValueField.set(base, isValueField.get(it))
} else {
criteriaChainField?.get(base)?.uncheckedCast<MutableList<Any>>()
?.addAll(criteriaChainField.get(it).uncheckedCast())
}
}
base
} else {
Criteria().andOperator(*criteriaList.toTypedArray())
}
}
get() = criteriaList.toAndCriteria(uniqueField)
val or: Criteria
get() = when (criteriaList.size) {
0 -> Criteria()
1 -> criteriaList.first()
else -> Criteria().orOperator(*criteriaList.toTypedArray())
}
get() = criteriaList.toOrCriteria()
private val criteriaList = ArrayList<Criteria>()
@ -103,8 +60,7 @@ class QueryBuilder : MongoName {
fun String.asObjectId() = ObjectId(this)
private fun String.asWhere() = Criteria.where(this).also { uniqueField = uniqueField && fieldSet.add(this) }
private fun KProperty<*>.asWhere() =
mongoName.asWhere().also { uniqueField = uniqueField && fieldSet.add(mongoName) }
private fun KProperty<*>.asWhere() = mongoName.asWhere()
//infix fun Criteria.or(operator: QueryBuilder.() -> Unit) {
// criteriaList.add(QueryBuilder().also(operator).criteria)
@ -122,6 +78,12 @@ class QueryBuilder : MongoName {
infix fun KProperty<*>.gte(where: Any) = asWhere() gte where // >=
fun KProperty<*>.`in`(vararg where: Any?) = asWhere() `in` where // contains
infix fun KProperty<*>.`in`(where: Collection<Any?>) = asWhere() `in` (where) // contains
infix operator fun <T> Collection<T>.contains(p: KProperty<T>): Boolean {
p `in` this
return true
}
infix fun KProperty<*>.`is`(where: Any?) = asWhere() `is` where // ==
infix fun KProperty<*>.ne(where: Any?) = asWhere() ne where // !=
infix fun KProperty<*>.lt(where: Any) = asWhere() lt where // <
@ -134,7 +96,7 @@ class QueryBuilder : MongoName {
infix fun KProperty<*>.size(s: Int) = asWhere() size s
infix fun KProperty<*>.exists(b: Boolean) = asWhere() exists b
infix fun KProperty<*>.type(t: Int) = asWhere() type t
operator fun KProperty<*>.not() = asWhere().not().last(criteriaList::add)
operator fun KProperty<*>.not() = asWhere().not().run(criteriaList::add)
infix fun KProperty<*>.regex(re: String) = asWhere() regex re
infix fun KProperty<*>.regex(pattern: Pattern) = asWhere() regex pattern
infix fun KProperty<*>.regex(pattern: Regex) = asWhere() regex pattern
@ -157,6 +119,45 @@ class QueryBuilder : MongoName {
infix fun KProperty<*>.equal(where: QueryBuilder.() -> Unit) = asWhere() equal QueryBuilder(where).queryObject
infix fun KProperty<*>.eq(where: QueryBuilder.() -> Unit) = asWhere() equal QueryBuilder(where).queryObject
infix fun StringBuilder.gt(where: Any) = toString() gt where // >
infix fun StringBuilder.gte(where: Any) = toString() gte where // >=
fun StringBuilder.`in`(vararg where: Any?) = toString().`in`(where = where) // contains
infix fun StringBuilder.`in`(where: Collection<Any?>) = toString() `in` where // contains
infix fun StringBuilder.`is`(where: Any?) = toString() `is` where // ==
infix fun StringBuilder.ne(where: Any?) = toString() ne where // !=
infix fun StringBuilder.lt(where: Any) = toString() lt where // <
infix fun StringBuilder.lte(where: Any) = toString() lte where // <=
fun StringBuilder.nin(vararg where: Any?) = toString().nin(where = where) // not contains
infix fun StringBuilder.nin(where: Collection<Any?>) = toString() nin where // not contains
infix fun StringBuilder.all(where: Any?) = toString() all where
infix fun StringBuilder.all(where: Collection<Any?>) = toString() all where
infix fun StringBuilder.size(s: Int) = toString() size s
infix fun StringBuilder.exists(b: Boolean) = toString() exists b
infix fun StringBuilder.type(t: Int) = toString() type t
operator fun StringBuilder.not() = toString().not()
infix fun StringBuilder.regex(re: String) = toString() regex re
infix fun StringBuilder.regex(pattern: Pattern) = toString() regex pattern
infix fun StringBuilder.regex(pattern: Regex) = toString() regex pattern.toPattern()
infix fun StringBuilder.regex(regex: BsonRegularExpression) = toString() regex regex
infix fun StringBuilder.withinSphere(circle: Circle) = toString() withinSphere circle
infix fun StringBuilder.within(shape: Shape) = toString() within shape
infix fun StringBuilder.near(point: Point) = toString() near point
infix fun StringBuilder.nearSphere(point: Point) = toString() nearSphere point
infix fun StringBuilder.intersects(point: GeoJson<*>) = toString() intersects point
infix fun StringBuilder.maxDistance(maxDistance: Double) = toString() maxDistance maxDistance
infix fun StringBuilder.minDistance(maxDistance: Double) = toString() minDistance maxDistance
infix fun StringBuilder.elemMatch(c: QueryBuilder.() -> Unit) = toString() elemMatch c
infix fun StringBuilder.alike(sample: Example<*>) = toString() alike sample
infix fun StringBuilder.andDocumentStructureMatches(schema: MongoJsonSchema) =
toString() andDocumentStructureMatches schema
infix fun StringBuilder.equal(where: Any?) = toString() equal where
infix fun StringBuilder.eq(where: Any?) = toString() equal where
infix fun StringBuilder.equal(where: QueryBuilder.() -> Unit) = toString() equal QueryBuilder(where).queryObject
infix fun StringBuilder.eq(where: QueryBuilder.() -> Unit) = toString() equal QueryBuilder(where).queryObject
infix fun String.gt(where: Any) = asWhere() gt where // >
infix fun String.gte(where: Any) = asWhere() gte where // >=
fun String.`in`(vararg where: Any?) = asWhere() `in` where // contains
@ -173,7 +174,7 @@ class QueryBuilder : MongoName {
infix fun String.size(s: Int) = asWhere() size s
infix fun String.exists(b: Boolean) = asWhere() exists b
infix fun String.type(t: Int) = asWhere() type t
operator fun String.not() = asWhere().not().last(criteriaList::add)
operator fun String.not() = asWhere().not().run(criteriaList::add)
infix fun String.regex(re: String) = asWhere() regex re
infix fun String.regex(pattern: Pattern) = asWhere() regex pattern
infix fun String.regex(pattern: Regex) = asWhere() regex pattern.toPattern()
@ -195,46 +196,46 @@ class QueryBuilder : MongoName {
infix fun String.equal(where: QueryBuilder.() -> Unit) = asWhere() equal QueryBuilder(where).queryObject
infix fun String.eq(where: QueryBuilder.() -> Unit) = asWhere() equal QueryBuilder(where).queryObject
private infix fun Criteria.gt(where: Any) = gt(where).last(criteriaList::add)
private infix fun Criteria.gt(where: Any) = gt(where).run(criteriaList::add)
private infix fun Criteria.gte(where: Any) = gte(where).last(criteriaList::add)
private infix fun Criteria.gte(where: Any) = gte(where).run(criteriaList::add)
private infix fun Criteria.`in`(where: Array<out Any?>) = `in`(where).last(criteriaList::add)
private infix fun Criteria.`in`(where: Collection<Any?>) = `in`(where).last(criteriaList::add)
private infix fun Criteria.`in`(where: Array<out Any?>) = `in`(where).run(criteriaList::add)
private infix fun Criteria.`in`(where: Collection<Any?>) = `in`(where).run(criteriaList::add)
private infix fun Criteria.`is`(where: Any?) = `is`(where).last(criteriaList::add)
private infix fun Criteria.ne(where: Any?) = ne(where).last(criteriaList::add)
private infix fun Criteria.lt(where: Any) = lt(where).last(criteriaList::add)
private infix fun Criteria.lte(where: Any) = lte(where).last(criteriaList::add)
private infix fun Criteria.nin(where: Array<out Any?>) = nin(where).last(criteriaList::add)
private infix fun Criteria.nin(where: Collection<Any?>) = nin(where).last(criteriaList::add)
private infix fun Criteria.`is`(where: Any?) = `is`(where).run(criteriaList::add)
private infix fun Criteria.ne(where: Any?) = ne(where).run(criteriaList::add)
private infix fun Criteria.lt(where: Any) = lt(where).run(criteriaList::add)
private infix fun Criteria.lte(where: Any) = lte(where).run(criteriaList::add)
private infix fun Criteria.nin(where: Array<out Any?>) = nin(where).run(criteriaList::add)
private infix fun Criteria.nin(where: Collection<Any?>) = nin(where).run(criteriaList::add)
private infix fun Criteria.all(where: Any?) = all(where).last(criteriaList::add)
private infix fun Criteria.all(where: Collection<Any?>) = all(where).last(criteriaList::add)
private infix fun Criteria.size(s: Int) = size(s).last(criteriaList::add)
private infix fun Criteria.exists(b: Boolean) = exists(b).last(criteriaList::add)
private infix fun Criteria.type(t: Int) = type(t).last(criteriaList::add)
private infix fun Criteria.all(where: Any?) = all(where).run(criteriaList::add)
private infix fun Criteria.all(where: Collection<Any?>) = all(where).run(criteriaList::add)
private infix fun Criteria.size(s: Int) = size(s).run(criteriaList::add)
private infix fun Criteria.exists(b: Boolean) = exists(b).run(criteriaList::add)
private infix fun Criteria.type(t: Int) = type(t).run(criteriaList::add)
//private operator fun Criteria.not() = not().last(criteriaList::add)
private infix fun Criteria.regex(re: String) = regex(re).last(criteriaList::add)
private infix fun Criteria.regex(pattern: Pattern) = regex(pattern).last(criteriaList::add)
private infix fun Criteria.regex(pattern: Regex) = regex(pattern.toPattern()).last(criteriaList::add)
private infix fun Criteria.regex(regex: BsonRegularExpression) = regex(regex).last(criteriaList::add)
private infix fun Criteria.withinSphere(circle: Circle) = withinSphere(circle).last(criteriaList::add)
private infix fun Criteria.within(shape: Shape) = within(shape).last(criteriaList::add)
private infix fun Criteria.near(point: Point) = near(point).last(criteriaList::add)
private infix fun Criteria.nearSphere(point: Point) = nearSphere(point).last(criteriaList::add)
private infix fun Criteria.intersects(point: GeoJson<*>) = intersects(point).last(criteriaList::add)
private infix fun Criteria.maxDistance(maxDistance: Double) = maxDistance(maxDistance).last(criteriaList::add)
private infix fun Criteria.minDistance(maxDistance: Double) = minDistance(maxDistance).last(criteriaList::add)
//private operator fun Criteria.not() = not().run(criteriaList::add)
private infix fun Criteria.regex(re: String) = regex(re).run(criteriaList::add)
private infix fun Criteria.regex(pattern: Pattern) = regex(pattern).run(criteriaList::add)
private infix fun Criteria.regex(pattern: Regex) = regex(pattern.toPattern()).run(criteriaList::add)
private infix fun Criteria.regex(regex: BsonRegularExpression) = regex(regex).run(criteriaList::add)
private infix fun Criteria.withinSphere(circle: Circle) = withinSphere(circle).run(criteriaList::add)
private infix fun Criteria.within(shape: Shape) = within(shape).run(criteriaList::add)
private infix fun Criteria.near(point: Point) = near(point).run(criteriaList::add)
private infix fun Criteria.nearSphere(point: Point) = nearSphere(point).run(criteriaList::add)
private infix fun Criteria.intersects(point: GeoJson<*>) = intersects(point).run(criteriaList::add)
private infix fun Criteria.maxDistance(maxDistance: Double) = maxDistance(maxDistance).run(criteriaList::add)
private infix fun Criteria.minDistance(maxDistance: Double) = minDistance(maxDistance).run(criteriaList::add)
private infix fun Criteria.elemMatch(c: QueryBuilder.() -> Unit) =
elemMatch(QueryBuilder criteria c).last(criteriaList::add)
elemMatch(QueryBuilder criteria c).run(criteriaList::add)
private infix fun Criteria.alike(sample: Example<*>) = alike(sample).last(criteriaList::add)
private infix fun Criteria.alike(sample: Example<*>) = alike(sample).run(criteriaList::add)
private infix fun Criteria.andDocumentStructureMatches(schema: MongoJsonSchema) =
andDocumentStructureMatches(schema).last(criteriaList::add)
andDocumentStructureMatches(schema).run(criteriaList::add)
private infix fun Criteria.equal(where: Any?) = `is`(where).last(criteriaList::add)
private infix fun Criteria.equal(where: Any?) = `is`(where).run(criteriaList::add)
/**
* 根据 builder 所指定的规则排序
@ -244,7 +245,7 @@ class QueryBuilder : MongoName {
/**
* 取以 field 为基准逆序排序的第一个元素
*/
infix fun Query.last(field: KProperty<*>) = apply {
infix fun Query.run(field: KProperty<*>) = apply {
sort { field order Sort.Direction.DESC }
limit(1)
}
@ -297,7 +298,7 @@ class QueryBuilder : MongoName {
limit(1)
}
infix fun Query.last(field: String) = apply {
infix fun Query.run(field: String) = apply {
sort { field order Sort.Direction.DESC }
limit(1)
}
@ -321,11 +322,61 @@ class QueryBuilder : MongoName {
limit(1)
}
fun Query.page(page: Long = 0, pageSize: Int = 0, defaultPageSize: Int = 50) {
@Suppress("NAME_SHADOWING")
val pageSize = if (pageSize > 0) pageSize else defaultPageSize
if (page > 1) {
skip((page - 1) * pageSize)
}
limit(pageSize)
}
val Query.fields get() = fields()
fun Query.include(vararg keys: KProperty<*>): Field = fields.include(keys = keys)
fun Query.exclude(vararg keys: KProperty<*>): Field = fields.exclude(keys = keys)
fun Query.include(key: KProperty<*>) = fields.include(key.mongoName)
fun Query.exclude(key: KProperty<*>) = fields.exclude(key.mongoName)
fun Query.slice(key: KProperty<*>, size: Int) = fields.slice(key.mongoName, size)
fun Query.slice(key: KProperty<*>, offset: Int, size: Int) = fields.slice(key.mongoName, offset, size)
fun Query.position(field: KProperty<*>, value: Int) = fields.position(mongoName(field), value)
fun Query.elemMatch(key: KProperty<*>, elemMatchCriteria: Criteria) =
fields.elemMatch(key.mongoName, elemMatchCriteria)
fun Field.include(vararg keys: KProperty<*>): Field {
keys.forEach { key ->
include(key)
}
return this
}
fun Field.exclude(vararg keys: KProperty<*>): Field {
keys.forEach { key ->
exclude(key)
}
return this
}
infix fun Field.include(key: KProperty<*>) = include(key.mongoName)
infix fun Field.exclude(key: KProperty<*>) = exclude(key.mongoName)
fun Field.slice(key: KProperty<*>, size: Int) = slice(key.mongoName, size)
fun Field.slice(key: KProperty<*>, offset: Int, size: Int) = slice(key.mongoName, offset, size)
fun Field.elemMatch(key: KProperty<*>, elemMatchCriteria: Criteria) = elemMatch(key.mongoName, elemMatchCriteria)
fun Field.position(field: KProperty<*>, value: Int) = position(mongoName(field), value)
infix fun KProperty<*>.regexIfNotBlank(re: String?) = mongoName regexIfNotEmpty re
infix fun String.regexIfNotBlank(re: String?) = if (re != null && re.isNotBlank()) {
this regex re
true
} else {
false
}
infix fun KProperty<*>.regexIfNotEmpty(re: String?) = mongoName regexIfNotEmpty re
infix fun String.regexIfNotEmpty(re: String?) = if (re != null && re.isNotBlank()) {
this regex re
true
} else {
false
}
}

View File

@ -19,11 +19,12 @@ class SortBuilder : MongoName, BsonConverter {
operator fun invoke(action: SortBuilder.() -> Unit) = this.action()
infix fun String.order(direction: Sort.Direction): Sort = Sort.by(Sort.Order(direction, this)).apply {
infix fun String.order(direction: Sort.Direction) = Sort.by(direction, this).apply {
updateSort(this) { this.and(it) }
}
infix fun KProperty<*>.order(direction: Sort.Direction) = mongoName order direction
infix fun StringBuilder.order(direction: Sort.Direction) = toString() order direction
fun by(property: String) = Sort.by(property).apply {
updateSort(this) { this.and(it) }
@ -38,4 +39,4 @@ class SortBuilder : MongoName, BsonConverter {
this.sort = action(this.sort!!)
}
}
}
}

View File

@ -1,5 +1,6 @@
package cn.tursom.database.mongodb.spring
import cn.tursom.core.isNotNullOrEmpty
import org.bson.Document
import org.springframework.data.mongodb.core.query.Update
import kotlin.reflect.KProperty
@ -32,6 +33,21 @@ class UpdateBuilder(val update: Update = Update()) : MongoName, BsonConverter {
infix fun String.set(value: Any?) = update.set(this, value?.bsonValue())
infix fun String.setOnInsert(value: Any?) = update.setOnInsert(this, value?.bsonValue())
infix fun String.set(value: ConcatBuilder.() -> Unit) =
update.set(this, ConcatBuilder(value).toDocument())
fun String.set(vararg value: Any?) = this set {
for (any in value) {
+any
}
}
fun String.set(value: Iterable<*>) = this set {
for (any in value) {
+any
}
}
fun String.unset() = update.unset(this)
infix fun String.inc(inc: Number) = update.inc(this, inc)
fun String.inc() = update.inc(this, 1)
@ -44,6 +60,8 @@ class UpdateBuilder(val update: Update = Update()) : MongoName, BsonConverter {
ReplaceWith("push")
)
infix fun String.pushAll(values: Array<out Any?>) = update.pushAll(this, values)
infix fun String.pushAll(values: List<Any?>) = update.push(this).each(values.toTypedArray())
infix fun String.pushAll(values: Sequence<Any?>) = this pushAll values.toList()
fun String.addToSet() = update.addToSet(this)
infix fun String.addToSet(value: Any?) = update.addToSet(this, value?.bsonValue())
@ -61,9 +79,75 @@ class UpdateBuilder(val update: Update = Update()) : MongoName, BsonConverter {
fun String.bitwise() = update.bitwise(this)
fun String.modifies() = update.modifies(this)
infix fun StringBuilder.set(value: Any?) = update.set(toString(), value?.bsonValue())
infix fun StringBuilder.setOnInsert(value: Any?) = update.setOnInsert(toString(), value?.bsonValue())
infix fun StringBuilder.set(value: ConcatBuilder.() -> Unit) =
update.set(toString(), ConcatBuilder(value).toDocument())
fun StringBuilder.set(vararg value: Any?) = this set {
for (any in value) {
+any
}
}
fun StringBuilder.set(value: Iterable<*>) = this set {
for (any in value) {
+any
}
}
fun StringBuilder.unset() = update.unset(toString())
infix fun StringBuilder.inc(inc: Number) = update.inc(toString(), inc)
fun StringBuilder.inc() = update.inc(toString(), 1)
infix fun StringBuilder.push(value: Any?) = update.push(toString(), value?.bsonValue())
fun StringBuilder.push() = update.push(toString())
@Suppress("DEPRECATION")
@Deprecated(
"as of MongoDB 2.4. Removed in MongoDB 3.6. Use {@link #push(String) \$push \$each} instead.",
ReplaceWith("push")
)
infix fun StringBuilder.pushAll(values: Array<out Any?>) = update.pushAll(toString(), values)
infix fun StringBuilder.pushAll(values: List<Any?>) = update.push(toString()).each(values.toTypedArray())
infix fun StringBuilder.pushAll(values: Sequence<Any?>) = toString() pushAll values.toList()
fun StringBuilder.addToSet() = update.addToSet(toString())
infix fun StringBuilder.addToSet(value: Any?) = update.addToSet(toString(), value?.bsonValue())
infix fun StringBuilder.pop(pos: Update.Position) = update.pop(toString(), pos)
infix fun StringBuilder.pull(value: Any?) = update.pull(toString(), value?.bsonValue())
infix fun StringBuilder.pull(query: QueryBuilder.() -> Unit) =
update.pull(toString(), QueryBuilder queryObject query)
infix fun StringBuilder.pullAll(values: Array<out Any?>) = update.pullAll(toString(), values)
infix fun StringBuilder.pullAll(values: Iterable<Any?>) =
update.addMultiFieldOperation("\$pullAll", toString(), values)
infix fun StringBuilder.rename(newName: String) = update.rename(toString(), newName)
fun StringBuilder.currentDate() = update.currentDate(toString())
fun StringBuilder.currentTimestamp() = update.currentTimestamp(toString())
infix fun StringBuilder.multiply(multiplier: Number) = update.multiply(toString(), multiplier)
infix fun StringBuilder.max(value: Any) = update.max(toString(), value.bsonValue())
infix fun StringBuilder.min(value: Any) = update.min(toString(), value.bsonValue())
fun StringBuilder.bitwise() = update.bitwise(toString())
fun StringBuilder.modifies() = update.modifies(toString())
infix fun <T> KProperty<T?>.set(value: T) = mongoName set value
infix fun <T> KProperty<T?>.update(value: T) = mongoName set value
infix fun <T> KProperty<T?>.setOnInsert(value: T) = mongoName setOnInsert value
infix fun KProperty<*>.set(value: ConcatBuilder.() -> Unit) = mongoName set value
fun KProperty<*>.set(vararg value: Any?) = mongoName set {
for (any in value) {
+any
}
}
fun KProperty<*>.set(value: Iterable<*>) = mongoName set {
for (any in value) {
+any
}
}
fun KProperty<*>.unset() = mongoName.unset()
infix fun KProperty<*>.inc(inc: Number) = mongoName inc inc
fun KProperty<*>.inc() = mongoName inc (1)
@ -76,6 +160,8 @@ class UpdateBuilder(val update: Update = Update()) : MongoName, BsonConverter {
ReplaceWith("push")
)
infix fun <T> KProperty<T>.pushAll(values: Array<T>) = mongoName pushAll values
inline infix fun <reified T> KProperty<T>.pushAll(values: List<T>) = mongoName.push().each(*values.toTypedArray())
inline infix fun <reified T> KProperty<T>.pushAll(values: Sequence<T>) = mongoName pushAll values.toList()
fun KProperty<*>.addToSet() = mongoName.addToSet()
infix fun <T : Any> KProperty<Iterable<T>>.addToSet(value: T) = mongoName addToSet value
@ -106,4 +192,31 @@ class UpdateBuilder(val update: Update = Update()) : MongoName, BsonConverter {
infix fun KProperty<*>.min(value: Any) = mongoName min value
fun KProperty<*>.bitwise() = mongoName.bitwise()
fun KProperty<*>.modifies() = mongoName.modifies()
infix fun KProperty<*>.setIfNotEmpty(value: String?) = mongoName setIfNotEmpty value
infix fun String.setIfNotEmpty(value: String?): Update {
return if (value.isNotNullOrEmpty()) {
update.set(this, value)
} else {
update
}
}
infix fun KProperty<*>.setIfNotBlank(value: String?) = mongoName setIfNotBlank value
infix fun String.setIfNotBlank(value: String?): Update {
return if (value != null && value.isNotBlank()) {
update.set(this, value)
} else {
update
}
}
infix fun KProperty<*>.setIfNotNull(value: Any?) = mongoName setIfNotNull value
infix fun String.setIfNotNull(value: Any?): Update {
return if (value != null) {
this set value
} else {
update
}
}
}

View File

@ -8,7 +8,8 @@ import org.bson.conversions.Bson
@Suppress("MemberVisibilityCanBePrivate", "unused")
class UpdateOption {
companion object {
operator fun invoke(builder: UpdateOption.() -> Unit): UpdateOptions = UpdateOption().apply(builder).updateOptions
inline operator fun invoke(builder: UpdateOption.() -> Unit): UpdateOptions =
UpdateOption().apply(builder).updateOptions
}
val updateOptions = UpdateOptions()

View File

@ -2,77 +2,374 @@
package cn.tursom.database.mongodb.spring
import cn.tursom.core.clone.clone
import cn.tursom.core.toSetNotNull
import cn.tursom.core.uncheckedCast
import com.mongodb.client.model.FindOneAndUpdateOptions
import com.mongodb.client.result.DeleteResult
import com.mongodb.client.result.UpdateResult
import org.bson.Document
import org.bson.conversions.Bson
import org.springframework.data.mongodb.core.FindAndModifyOptions
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.aggregation.AggregationExpression
import org.springframework.data.mongodb.core.getCollectionName
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Field
import org.springframework.data.mongodb.core.query.Query
import org.springframework.data.mongodb.core.query.Update
import kotlin.reflect.KProperty
internal val criteriaField: java.lang.reflect.Field? = try {
val field = Criteria::class.java.getDeclaredField("criteria")
field.isAccessible = true
field
} catch (e: Exception) {
null
}
internal val isValueField: java.lang.reflect.Field? = try {
val field = Criteria::class.java.getDeclaredField("isValue")
field.isAccessible = true
field
} catch (e: Exception) {
null
}
internal val criteriaChainField: java.lang.reflect.Field? = try {
val field = Criteria::class.java.getDeclaredField("criteriaChain")
field.isAccessible = true
field
} catch (e: Exception) {
null
}
fun List<Criteria>.toAndCriteria(
uniqueField: Boolean = toSetNotNull { it.key }.size == size
) = when (size) {
0 -> Criteria()
1 -> first()
else -> if (uniqueField && criteriaField != null && isValueField != null) {
var base = Criteria()
forEach {
if (it.key != null) {
base = base.and(it.key!!)
criteriaField.set(base, criteriaField.get(it))
isValueField.set(base, isValueField.get(it))
} else {
criteriaChainField?.get(base)?.uncheckedCast<MutableList<Any>>()
?.addAll(criteriaChainField.get(it).uncheckedCast())
}
}
base
} else {
Criteria().andOperator(*toTypedArray())
}
}!!
fun List<Criteria>.toOrCriteria() = when (size) {
0 -> Criteria()
1 -> first()
else -> Criteria().orOperator(*toTypedArray())
}!!
fun AggregationExpression.toDocument(): Document = toDocument(DirectAggregationOperationContext)
fun <T : Any> MongoTemplate.read(bson: Bson, type: Class<T>): T = converter.read(type, bson)
inline fun <reified T : Any> MongoTemplate.read(bson: Bson): T = read(bson, T::class.java)
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
fun <T : Any> MongoTemplate.readNullable(bson: Bson?, type: Class<T>): T? = converter.read(type, bson)
inline fun <reified T : Any> MongoTemplate.readNullable(bson: Bson?): T? = readNullable(bson, T::class.java)
inline fun <reified T : Any> MongoTemplate.count(query: Query) = count(query, T::class.java)
inline fun <reified T : Any> MongoTemplate.count(
noinline operator: QueryBuilder.() -> Unit,
operator: QueryBuilder.() -> Unit,
) = count(QueryBuilder(operator), T::class.java)
inline fun <reified T : Any> MongoTemplate.count(
collectionName: String,
operator: QueryBuilder.() -> Unit,
) = count(QueryBuilder(operator), T::class.java, collectionName)
inline fun <reified T : Any> MongoTemplate.find(query: Query): List<T> = find(query, T::class.java)
inline fun <reified T : Any> MongoTemplate.find(
noinline operator: QueryBuilder.() -> Unit,
operator: QueryBuilder.() -> Unit,
): List<T> = find(QueryBuilder(operator), T::class.java)
inline fun <reified T : Any> MongoTemplate.find(
collectionName: String,
operator: QueryBuilder.() -> Unit,
): List<T> = find(QueryBuilder(operator), T::class.java, collectionName)
inline fun <reified T : Any> MongoTemplate.findOne(query: Query): T? = findOne(query, T::class.java)
inline fun <reified T : Any> MongoTemplate.findOne(
noinline operator: QueryBuilder.() -> Unit,
operator: QueryBuilder.() -> Unit,
): T? = findOne(QueryBuilder(operator), T::class.java)
//inline fun <T, R> T.use(action: (T) -> R): T {
// action(this)
// return this
//}
inline fun <T, R> T.last(action: (T) -> R) {
action(this)
}
inline fun <reified T : Any> MongoTemplate.findOne(
collectionName: String,
operator: QueryBuilder.() -> Unit,
): T? = findOne(QueryBuilder(operator), T::class.java, collectionName)
inline fun <reified T : Any> MongoTemplate.upsert(
query: Query,
update: Update,
): UpdateResult = upsert(query, update, T::class.java)
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = upsert(QueryBuilder(query), UpdateBuilder(update), T::class.java)
inline fun <reified T : Any> MongoTemplate.upsert(
collectionName: String,
query: Query,
update: Update,
): UpdateResult = upsert(query, update, T::class.java, collectionName)
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = upsert(QueryBuilder(query), UpdateBuilder(update), T::class.java, collectionName)
inline fun <reified T : Any> MongoTemplate.updateFirst(
query: Query,
update: Update,
): UpdateResult = updateFirst(query, update, T::class.java)
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = updateFirst(QueryBuilder(query), UpdateBuilder(update), T::class.java)
inline fun <reified T : Any> MongoTemplate.updateFirst(
collectionName: String,
query: Query,
update: Update,
): UpdateResult = updateFirst(query, update, T::class.java, collectionName)
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = updateFirst(QueryBuilder(query), UpdateBuilder(update), T::class.java, collectionName)
inline fun <reified T : Any> MongoTemplate.updateFirst(
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
options: UpdateOption.() -> Unit,
): UpdateResult = updateFirst<T>(getCollectionName(T::class.java), query, update, options)
inline fun <reified T : Any> MongoTemplate.updateFirst(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
options: UpdateOption.() -> Unit,
): UpdateResult = db.getCollection(collectionName).updateOne(
QueryBuilder queryObject query,
UpdateBuilder updateObject update,
UpdateOption(options)
)
inline fun <reified T : Any> MongoTemplate.aggregationUpdateFirst(
query: QueryBuilder.() -> Unit,
vararg update: UpdateBuilder.() -> Unit,
): UpdateResult = aggregationUpdateFirst<T>(getCollectionName(T::class.java), query, update = update)
inline fun <reified T : Any> MongoTemplate.aggregationUpdateFirst(
collectionName: String,
query: QueryBuilder.() -> Unit,
vararg update: UpdateBuilder.() -> Unit,
): UpdateResult = db.getCollection(collectionName).updateOne(
QueryBuilder queryObject query,
update.map { UpdateBuilder updateObject it },
)
inline fun <reified T : Any> MongoTemplate.aggregationUpdateFirst(
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = db.getCollection(getCollectionName(T::class.java)).updateOne(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update)
)
inline fun <reified T : Any> MongoTemplate.aggregationUpdateFirst(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = db.getCollection(collectionName).updateOne(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update)
)
inline fun <reified T : Any> MongoTemplate.updateMulti(
query: Query,
update: Update,
): UpdateResult = updateMulti(query, update, T::class.java)
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = updateMulti(QueryBuilder(query), UpdateBuilder(update), T::class.java)
inline fun <reified T : Any> MongoTemplate.updateMulti(
collectionName: String,
query: Query,
update: Update,
): UpdateResult = updateMulti(query, update, T::class.java, collectionName)
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = updateMulti(QueryBuilder(query), UpdateBuilder(update), T::class.java, collectionName)
inline fun <reified T : Any> MongoTemplate.updateMulti(
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
options: UpdateOption.() -> Unit,
): UpdateResult = updateMulti<T>(getCollectionName(T::class.java), query, update, options)
inline fun <reified T : Any> MongoTemplate.updateMulti(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
options: UpdateOption.() -> Unit,
): UpdateResult = db.getCollection(collectionName).updateMany(
QueryBuilder queryObject query,
UpdateBuilder updateObject update,
UpdateOption(options)
)
inline fun <reified T : Any> MongoTemplate.aggregationUpdateMulti(
query: QueryBuilder.() -> Unit,
vararg update: UpdateBuilder.() -> Unit,
): UpdateResult = aggregationUpdateMulti<T>(getCollectionName<T>(), query, update = update)
inline fun <reified T : Any> MongoTemplate.aggregationUpdateMulti(
collectionName: String,
query: QueryBuilder.() -> Unit,
vararg update: UpdateBuilder.() -> Unit,
): UpdateResult = db.getCollection(collectionName).updateMany(
QueryBuilder queryObject query,
update.map { UpdateBuilder updateObject it }
)
inline fun <reified T : Any> MongoTemplate.aggregationUpdateMulti(
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = db.getCollection(getCollectionName(T::class.java)).updateMany(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update)
)
inline fun <reified T : Any> MongoTemplate.aggregationUpdateMulti(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): UpdateResult = db.getCollection(collectionName).updateMany(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update)
)
inline fun <reified T : Any> MongoTemplate.findAndModify(
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): T? = findAndModify(QueryBuilder(query), UpdateBuilder(update), T::class.java)
inline fun <reified T : Any> MongoTemplate.findAndModify(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): T? = findAndModify(QueryBuilder(query), UpdateBuilder(update), T::class.java, collectionName)
inline fun <reified T : Any> MongoTemplate.findAndModify(
collectionName: String,
options: FindAndModifyOptions,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): T? = findAndModify(QueryBuilder(query), UpdateBuilder(update), options, T::class.java, collectionName)
inline fun <reified T : Any> MongoTemplate.findAndModify(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
options: FindAndModifyOptions.() -> Unit,
): T? = findAndModify(
QueryBuilder(query),
UpdateBuilder(update),
FindAndModifyOptions().apply(options),
T::class.java,
collectionName
)
inline fun <reified T : Any> MongoTemplate.findAndModify(
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
options: FindAndModifyOptions.() -> Unit,
): T? = findAndModify(
QueryBuilder(query),
UpdateBuilder(update),
FindAndModifyOptions().apply(options),
T::class.java,
getCollectionName(T::class.java)
)
/**
* 提供 spring data mongo 无法提供的某些功能比如 arrayFilter
*/
inline fun <reified T : Any> MongoTemplate.findOneAndUpdate(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
updateOption: FindOneAndUpdateOptions.() -> Unit,
): T? = readNullable(
db.getCollection(collectionName).findOneAndUpdate(
QueryBuilder queryObject query,
UpdateBuilder updateObject update,
FindOneAndUpdateOptions().also(updateOption)
)
)
inline fun <reified T : Any> MongoTemplate.findOneAndUpdate(
collectionName: String,
query: QueryBuilder.() -> Unit,
vararg update: UpdateBuilder.() -> Unit,
updateOption: FindOneAndUpdateOptions.() -> Unit,
): T? = readNullable(
db.getCollection(collectionName).findOneAndUpdate(
QueryBuilder queryObject query,
update.map { UpdateBuilder updateObject it },
FindOneAndUpdateOptions().also(updateOption)
)
)
inline fun <reified T : Any> MongoTemplate.aggregationFindAndModify(
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): T? = db.getCollection(getCollectionName(T::class.java)).findOneAndUpdate(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update)
)?.clone()
inline fun <reified T : Any> MongoTemplate.aggregationFindAndModify(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): T? = db.getCollection(collectionName).findOneAndUpdate(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update)
)?.clone()
inline fun <reified T : Any> MongoTemplate.aggregationFindAndModify(
collectionName: String,
options: FindOneAndUpdateOptions,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
): T? = db.getCollection(collectionName).findOneAndUpdate(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update),
options
)?.clone()
inline fun <reified T : Any> MongoTemplate.aggregationFindAndModify(
collectionName: String,
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
options: FindOneAndUpdateOptions.() -> Unit,
): T? = db.getCollection(collectionName).findOneAndUpdate(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update),
FindOneAndUpdateOptions().apply(options)
)?.clone()
inline fun <reified T : Any> MongoTemplate.aggregationFindAndModify(
query: QueryBuilder.() -> Unit,
update: UpdateBuilder.() -> Unit,
options: FindOneAndUpdateOptions.() -> Unit,
): T? = db.getCollection(getCollectionName(T::class.java)).findOneAndUpdate(
QueryBuilder queryObject query,
listOf(UpdateBuilder updateObject update),
FindOneAndUpdateOptions().apply(options)
)?.clone()
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
inline fun <reified T : Any> MongoTemplate.remove(
queryBuilder: QueryBuilder.() -> Unit,
): DeleteResult = remove(QueryBuilder(queryBuilder), T::class.java)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
fun MongoTemplate.remove(
collectionName: String,
queryBuilder: QueryBuilder.() -> Unit,
): DeleteResult = remove(QueryBuilder(queryBuilder), collectionName)
infix fun Query.sort(builder: SortBuilder.() -> Unit) {
with(SortBuilder(builder))
}
@ -84,4 +381,4 @@ fun Field.slice(key: KProperty<*>, offset: Int, size: Int) = slice(MongoName.mon
fun Field.elemMatch(key: KProperty<*>, elemMatchCriteria: Criteria) =
elemMatch(MongoName.mongoName(key), elemMatchCriteria)
fun Field.position(field: KProperty<*>, value: Int) = position(MongoName.mongoName(field), value)
fun Field.position(field: KProperty<*>, value: Int) = position(MongoName.mongoName(field), value)

View File

@ -1,10 +1,23 @@
package cn.tursom.database.mongodb.spring
import org.springframework.data.mongodb.core.MongoTemplate
class UpdateBuilderTest {
data class Test(val a: String, val b: Int)
private lateinit var mongoTemplate: MongoTemplate
@org.junit.Test
fun testPush() {
mongoTemplate.updateMulti<Test>({
or {
Test::b eq 1
Test::b eq 2
}
}) {
Test::a push Test("a", 1)
Test::b set 1
}
println(UpdateBuilder {
Test::a push Test("a", 1)
Test::b set 1