mirror of
https://github.com/tursom/TursomServer.git
synced 2025-01-30 14:20:07 +08:00
update
This commit is contained in:
parent
887f04bb06
commit
720e7ceddf
@ -20,6 +20,8 @@ include("ts-web")
|
||||
include("ts-web:ts-web-netty")
|
||||
include("ts-web:ts-web-coroutine")
|
||||
include("ts-database")
|
||||
include("ts-database:ts-mongodb")
|
||||
include("ts-database:ts-mongodb:ts-mongodb-spring")
|
||||
//include("web", "aop", "database", "utils", "utils:xml", "utils:async-http", "web:netty-web")
|
||||
//include("socket", "socket:socket-async")
|
||||
//include("AsyncSocket")
|
||||
|
@ -0,0 +1,20 @@
|
||||
package cn.tursom.core.datastruct
|
||||
|
||||
interface AsyncIterator<T> {
|
||||
/**
|
||||
* Returns the next element in the iteration.
|
||||
*/
|
||||
suspend operator fun next(): T
|
||||
|
||||
/**
|
||||
* Returns `true` if the iteration has more elements.
|
||||
*/
|
||||
suspend operator fun hasNext(): Boolean
|
||||
}
|
||||
|
||||
suspend inline fun <T> AsyncIterator<T>.forEach(action: (T) -> Unit) {
|
||||
while (hasNext()) {
|
||||
val element = next()
|
||||
action(element)
|
||||
}
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
package cn.tursom.database
|
||||
|
||||
import cn.tursom.core.clone.Property
|
||||
import cn.tursom.core.clone.inject
|
||||
import cn.tursom.core.clone.instance
|
||||
import cn.tursom.core.uncheckedCast
|
||||
import com.baomidou.mybatisplus.annotation.TableField
|
||||
import com.ddbes.kotlin.clone.Property
|
||||
import com.ddbes.kotlin.clone.inject
|
||||
import com.ddbes.kotlin.clone.instance
|
||||
import com.ddbes.kotlin.jdbc.simpTableField
|
||||
import com.ddbes.kotlin.jdbc.tableName
|
||||
import com.ddbes.kotlin.uncheckedCast
|
||||
import me.liuwj.ktorm.dsl.QueryRowSet
|
||||
import me.liuwj.ktorm.schema.BaseTable
|
||||
import me.liuwj.ktorm.schema.Column
|
||||
|
36
ts-database/ts-mongodb/build.gradle.kts
Normal file
36
ts-database/ts-mongodb/build.gradle.kts
Normal file
@ -0,0 +1,36 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":"))
|
||||
implementation(project(":ts-core"))
|
||||
implementation(project(":ts-core:ts-datastruct"))
|
||||
implementation(project(":ts-core:ts-log"))
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")
|
||||
api(group = "org.mongodb", name = "mongodb-driver-reactivestreams", version = "4.0.5")
|
||||
}
|
||||
|
||||
@kotlin.Suppress("UNCHECKED_CAST")
|
||||
(rootProject.ext["excludeTest"] as (Project, TaskContainer) -> Unit)(project, tasks)
|
||||
|
||||
tasks.register("install") {
|
||||
finalizedBy(tasks["publishToMavenLocal"])
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
groupId = project.group.toString()
|
||||
artifactId = project.name
|
||||
version = project.version.toString()
|
||||
|
||||
from(components["java"])
|
||||
try {
|
||||
artifact(tasks["sourcesJar"])
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import com.mongodb.MongoNamespace
|
||||
import com.mongodb.client.model.*
|
||||
import org.bson.conversions.Bson
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
|
||||
@Suppress("unused")
|
||||
object Aggregate {
|
||||
operator fun invoke(action: Aggregate.() -> Bson) = this.action()
|
||||
|
||||
fun addFields(vararg fields: Field<*>): Bson = Aggregates.addFields(fields.asList())
|
||||
fun addFields(fields: List<Field<*>>): Bson? = Aggregates.addFields(fields)
|
||||
|
||||
fun <TExpression, Boundary> bucket(
|
||||
groupBy: TExpression,
|
||||
boundaries: List<Boundary>
|
||||
): Bson = Aggregates.bucket(groupBy, boundaries)
|
||||
|
||||
fun <TExpression, TBoundary> bucket(
|
||||
groupBy: TExpression,
|
||||
boundaries: List<TBoundary>,
|
||||
options: BucketOptions
|
||||
): Bson = Aggregates.bucket(groupBy, boundaries, options)
|
||||
|
||||
|
||||
fun <TExpression> bucketAuto(groupBy: TExpression, buckets: Int): Bson = Aggregates.bucketAuto(groupBy, buckets)
|
||||
|
||||
fun <TExpression> bucketAuto(
|
||||
groupBy: TExpression,
|
||||
buckets: Int,
|
||||
options: BucketAutoOptions
|
||||
): Bson = Aggregates.bucketAuto(groupBy, buckets, options)
|
||||
|
||||
fun count(): Bson = Aggregates.count()
|
||||
fun count(field: String): Bson = Aggregates.count(field)
|
||||
|
||||
fun match(filter: Bson): Bson = Aggregates.match(filter)
|
||||
fun match(value: Any): Bson = Aggregates.match(MongoUtil.convertToBson(value))
|
||||
fun project(projection: Bson): Bson = Aggregates.project(projection)
|
||||
fun sort(sort: Bson): Bson = Aggregates.sort(sort)
|
||||
fun <TExpression> sortByCount(filter: TExpression): Bson = Aggregates.sortByCount(filter)
|
||||
fun skip(skip: Int): Bson = Aggregates.skip(skip)
|
||||
fun limit(limit: Int): Bson = Aggregates.limit(limit)
|
||||
|
||||
fun lookup(
|
||||
from: String,
|
||||
localField: String,
|
||||
foreignField: String,
|
||||
`as`: String
|
||||
): Bson = Aggregates.lookup(from, localField, foreignField, `as`)
|
||||
|
||||
fun lookup(from: String, pipeline: List<Bson?>, `as`: String): Bson = Aggregates.lookup(from, pipeline, `as`)
|
||||
fun <TExpression> lookup(
|
||||
from: String,
|
||||
let: List<Variable<TExpression>>? = null,
|
||||
pipeline: List<Bson>,
|
||||
`as`: String
|
||||
): Bson = Aggregates.lookup(from, let, pipeline, `as`)
|
||||
|
||||
|
||||
fun facet(facets: List<Facet>): Bson = Aggregates.facet(facets)
|
||||
fun facet(vararg facets: Facet): Bson = Aggregates.facet(facets.asList())
|
||||
|
||||
fun <TExpression> graphLookup(
|
||||
from: String,
|
||||
startWith: TExpression,
|
||||
connectFromField: String,
|
||||
connectToField: String,
|
||||
`as`: String
|
||||
): Bson = Aggregates.graphLookup(from, startWith, connectFromField, connectToField, `as`)
|
||||
|
||||
fun <TExpression> graphLookup(
|
||||
from: String,
|
||||
startWith: TExpression,
|
||||
connectFromField: String,
|
||||
connectToField: String,
|
||||
`as`: String,
|
||||
options: GraphLookupOptions
|
||||
): Bson = Aggregates.graphLookup(from, startWith, connectFromField, connectToField, `as`, options)
|
||||
|
||||
|
||||
fun <TExpression> group(
|
||||
id: TExpression? = null,
|
||||
vararg fieldAccumulators: BsonField
|
||||
): Bson = Aggregates.group(id, fieldAccumulators.asList())
|
||||
|
||||
fun <TExpression> group(
|
||||
id: TExpression? = null,
|
||||
fieldAccumulators: List<BsonField>
|
||||
): Bson = Aggregates.group(id, fieldAccumulators)
|
||||
|
||||
fun unwind(field: KProperty1<*, *>): Bson = Aggregates.unwind(MongoUtil.fieldName(field))
|
||||
fun unwind(field: KProperty1<*, *>, unwindOptions: UnwindOptions): Bson =
|
||||
Aggregates.unwind(MongoUtil.fieldName(field), unwindOptions)
|
||||
|
||||
fun unwind(fieldName: String): Bson = Aggregates.unwind(fieldName)
|
||||
fun unwind(fieldName: String, unwindOptions: UnwindOptions): Bson = Aggregates.unwind(fieldName, unwindOptions)
|
||||
|
||||
fun out(collectionName: String): Bson = Aggregates.out(collectionName)
|
||||
|
||||
fun merge(collectionName: String): Bson = Aggregates.merge(collectionName)
|
||||
fun merge(namespace: MongoNamespace): Bson = Aggregates.merge(namespace)
|
||||
fun merge(collectionName: String, options: MergeOptions): Bson = Aggregates.merge(collectionName, options)
|
||||
fun merge(namespace: MongoNamespace, options: MergeOptions): Bson = Aggregates.merge(namespace, options)
|
||||
|
||||
fun <TExpression> replaceRoot(value: TExpression): Bson = Aggregates.replaceRoot(value)
|
||||
fun <TExpression> replaceWith(value: TExpression): Bson = Aggregates.replaceWith(value)
|
||||
fun sample(size: Int): Bson = Aggregates.sample(size)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import cn.tursom.core.Parser
|
||||
import org.bson.Document
|
||||
|
||||
interface BsonFactory<T> {
|
||||
val clazz: Class<T>
|
||||
|
||||
fun parse(document: Document) = Parser.parse(document, clazz)!!
|
||||
|
||||
fun convertToBson(entity: T): Document
|
||||
|
||||
//fun convertToEntity(bson: Document): T
|
||||
|
||||
companion object {
|
||||
operator fun <T> get(clazz: Class<T>): BsonFactory<T> {
|
||||
return BsonFactoryImpl(clazz)
|
||||
}
|
||||
|
||||
inline fun <reified T : Any?> get(): BsonFactory<T> {
|
||||
return get(T::class.java)
|
||||
}
|
||||
|
||||
fun convertToBson(entity: Any): Document {
|
||||
return get(entity.javaClass).convertToBson(entity)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import cn.tursom.core.isStatic
|
||||
import cn.tursom.core.isTransient
|
||||
import cn.tursom.database.mongodb.annotation.Ignore
|
||||
import org.bson.Document
|
||||
|
||||
open class BsonFactoryImpl<T>(final override val clazz: Class<T>) : BsonFactory<T> {
|
||||
private val fields = clazz.declaredFields.filter {
|
||||
it.isAccessible = true
|
||||
!it.isStatic() && !it.isTransient() && it.getAnnotation(Ignore::class.java) == null
|
||||
}
|
||||
|
||||
override fun convertToBson(entity: T): Document {
|
||||
val bson = Document()
|
||||
fields.forEach {
|
||||
MongoUtil.injectValue(bson, it.get(entity) ?: return@forEach, it)
|
||||
}
|
||||
return bson
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "BsonFactoryImpl(clazz=$clazz, fields=$fields)"
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import com.mongodb.client.model.Collation
|
||||
import com.mongodb.client.model.IndexOptions
|
||||
import org.bson.Document
|
||||
import org.bson.conversions.Bson
|
||||
import java.lang.reflect.Field
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("unused")
|
||||
class IndexBuilder {
|
||||
val indexDocument = Document()
|
||||
val indexOption = IndexOptions()
|
||||
|
||||
var background
|
||||
get() = indexOption.isBackground
|
||||
set(value) {
|
||||
indexOption.background(value)
|
||||
}
|
||||
var unique
|
||||
get() = indexOption.isUnique
|
||||
set(value) {
|
||||
indexOption.unique(value)
|
||||
}
|
||||
var name: String?
|
||||
get() = indexOption.name
|
||||
set(value) {
|
||||
indexOption.name(value)
|
||||
}
|
||||
var sparse
|
||||
get() = indexOption.isSparse
|
||||
set(value) {
|
||||
indexOption.sparse(value)
|
||||
}
|
||||
var expireAfterSeconds: Long?
|
||||
get() = indexOption.getExpireAfter(TimeUnit.SECONDS)
|
||||
set(value) {
|
||||
indexOption.expireAfter(value, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
fun getExpireAfter(timeUnit: TimeUnit): Long? = indexOption.getExpireAfter(timeUnit)
|
||||
fun expireAfter(expireAfter: Long?, timeUnit: TimeUnit): IndexOptions? =
|
||||
indexOption.expireAfter(expireAfter, timeUnit)
|
||||
|
||||
var version: Int?
|
||||
get() = indexOption.version
|
||||
set(value) {
|
||||
indexOption.version(value)
|
||||
}
|
||||
var weights: Bson?
|
||||
get() = indexOption.weights
|
||||
set(value) {
|
||||
indexOption.weights(value)
|
||||
}
|
||||
var defaultLanguage: String?
|
||||
get() = indexOption.defaultLanguage
|
||||
set(value) {
|
||||
indexOption.defaultLanguage(value)
|
||||
}
|
||||
var languageOverride: String?
|
||||
get() = indexOption.languageOverride
|
||||
set(value) {
|
||||
indexOption.languageOverride(value)
|
||||
}
|
||||
var textVersion: Int?
|
||||
get() = indexOption.textVersion
|
||||
set(value) {
|
||||
indexOption.textVersion(value)
|
||||
}
|
||||
var sphereVersion: Int?
|
||||
get() = indexOption.sphereVersion
|
||||
set(value) {
|
||||
indexOption.sphereVersion(value)
|
||||
}
|
||||
var bits: Int?
|
||||
get() = indexOption.bits
|
||||
set(value) {
|
||||
indexOption.bits(value)
|
||||
}
|
||||
var min: Double?
|
||||
get() = indexOption.min
|
||||
set(value) {
|
||||
indexOption.min(value)
|
||||
}
|
||||
var max: Double?
|
||||
get() = indexOption.max
|
||||
set(value) {
|
||||
indexOption.max(value)
|
||||
}
|
||||
var bucketSize: Double?
|
||||
get() = indexOption.bucketSize
|
||||
set(value) {
|
||||
indexOption.bucketSize(value)
|
||||
}
|
||||
var storageEngine: Bson?
|
||||
get() = indexOption.storageEngine
|
||||
set(value) {
|
||||
indexOption.storageEngine(value)
|
||||
}
|
||||
var partialFilterExpression: Bson?
|
||||
get() = indexOption.partialFilterExpression
|
||||
set(value) {
|
||||
indexOption.partialFilterExpression(value)
|
||||
}
|
||||
var collation: Collation?
|
||||
get() = indexOption.collation
|
||||
set(value) {
|
||||
indexOption.collation(value)
|
||||
}
|
||||
var wildcardProjection: Bson
|
||||
get() = indexOption.wildcardProjection
|
||||
set(value) {
|
||||
indexOption.wildcardProjection(value)
|
||||
}
|
||||
|
||||
|
||||
enum class SortEnum(val code: Int) {
|
||||
DSC(1), DESC(-1)
|
||||
}
|
||||
|
||||
infix fun String.index(sort: SortEnum) {
|
||||
indexDocument[this] = sort.code
|
||||
}
|
||||
|
||||
operator fun String.unaryPlus() = this index SortEnum.DSC
|
||||
operator fun String.unaryMinus() = this index SortEnum.DESC
|
||||
|
||||
infix fun KProperty<*>.index(sort: SortEnum) = MongoUtil.fieldName(this) index sort
|
||||
operator fun KProperty<*>.unaryPlus() = +MongoUtil.fieldName(this)
|
||||
operator fun KProperty<*>.unaryMinus() = -MongoUtil.fieldName(this)
|
||||
|
||||
infix fun Field.index(sort: SortEnum) = MongoUtil.fieldName(this) index sort
|
||||
operator fun Field.unaryPlus() = +MongoUtil.fieldName(this)
|
||||
operator fun Field.unaryMinus() = -MongoUtil.fieldName(this)
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import org.bson.Document
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
interface MongoName {
|
||||
val Class<*>.collectionName: String get() = MongoUtil.collectionName(this)
|
||||
|
||||
val Field.fieldName: String get() = MongoUtil.fieldName(this)
|
||||
|
||||
val KProperty<*>.fieldName get() = MongoUtil.fieldName(this)
|
||||
|
||||
val Any.convertToBson: Document get() = MongoUtil.convertToBson(this)
|
||||
|
||||
operator fun String.rem(field: String) = "$this.$field"
|
||||
operator fun String.rem(field: KProperty<*>) = "$this.${field.fieldName}"
|
||||
operator fun String.rem(field: Field) = "$this.${field.fieldName}"
|
||||
|
||||
operator fun KProperty<*>.rem(field: String) = "$fieldName.$field"
|
||||
operator fun KProperty<*>.rem(field: KProperty<*>) = "$fieldName.${field.fieldName}"
|
||||
operator fun KProperty<*>.rem(field: Field) = "$fieldName.${field.fieldName}"
|
||||
|
||||
operator fun Field.rem(field: String) = "$fieldName.$field"
|
||||
operator fun Field.rem(field: KProperty<*>) = "$fieldName.${field.fieldName}"
|
||||
operator fun Field.rem(field: Field) = "$fieldName.${field.fieldName}"
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import cn.tursom.core.uncheckedCast
|
||||
import cn.tursom.database.mongodb.operator.MongoOperator
|
||||
import cn.tursom.database.mongodb.operator.MongoOperatorImpl
|
||||
import com.mongodb.MongoClientSettings
|
||||
import com.mongodb.ServerAddress
|
||||
import com.mongodb.client.model.InsertManyOptions
|
||||
import com.mongodb.client.model.InsertOneOptions
|
||||
import com.mongodb.client.model.UpdateOptions
|
||||
import com.mongodb.reactivestreams.client.MongoClient
|
||||
import com.mongodb.reactivestreams.client.MongoClients
|
||||
import com.mongodb.reactivestreams.client.MongoDatabase
|
||||
import org.bson.conversions.Bson
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
class MongoTemplate(
|
||||
private val db: MongoDatabase,
|
||||
val subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : MongoDatabase by db {
|
||||
constructor(
|
||||
mongoClient: MongoClient,
|
||||
db: String,
|
||||
subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : this(mongoClient.getDatabase(db), subscribeExecutor)
|
||||
|
||||
constructor(
|
||||
db: String,
|
||||
mongoClientSettingsBuilder: MongoClientSettings.Builder,
|
||||
subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : this(MongoClients.create(mongoClientSettingsBuilder.build()), db, subscribeExecutor)
|
||||
|
||||
constructor(
|
||||
db: String,
|
||||
vararg servers: ServerAddress,
|
||||
subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : this(
|
||||
db,
|
||||
MongoClientSettings.builder().also {
|
||||
it.applyToClusterSettings { builder ->
|
||||
builder.hosts(servers.asList())
|
||||
}
|
||||
},
|
||||
subscribeExecutor
|
||||
)
|
||||
|
||||
constructor(host: String, port: Int, db: String) : this(db, ServerAddress(host, port))
|
||||
|
||||
private val operatorMap = ConcurrentHashMap<Class<*>, MongoOperatorImpl<*>>()
|
||||
|
||||
suspend fun <T : Any> save(entity: T, options: InsertOneOptions = InsertOneOptions()) =
|
||||
getCollection(entity.javaClass).save(entity, options)
|
||||
|
||||
suspend inline fun <reified T : Any> save(entities: Collection<T>, options: InsertManyOptions = InsertManyOptions()) =
|
||||
getCollection<T>().save(entities, options)
|
||||
|
||||
suspend fun <T : Any> update(entity: T, where: Bson, options: UpdateOptions = UpdateOptions()) =
|
||||
getCollection(entity.javaClass).update(entity, where, options)
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
suspend fun <T : Any> upsert(entity: T, where: Bson, options: UpdateOptions = UpdateOptions()) =
|
||||
getCollection(entity.javaClass).upsert(entity, where, options)
|
||||
|
||||
inline fun <reified T : Any> getCollection(): MongoOperator<T> = getCollection(T::class.java)
|
||||
|
||||
fun <T : Any> getCollection(clazz: Class<T>): MongoOperator<T> {
|
||||
return operatorMap[clazz]?.uncheckedCast() ?: run {
|
||||
val operator = MongoOperatorImpl(clazz, db, subscribeExecutor)
|
||||
operatorMap[clazz] = operator
|
||||
operator
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> getOperator(collection: String) = getOperator(collection, T::class.java)
|
||||
fun <T : Any> getOperator(collection: String, clazz: Class<T>): MongoOperator<T> {
|
||||
return MongoOperatorImpl(getCollection(collection), clazz, subscribeExecutor)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "MongoTemplate(db=$db)"
|
||||
}
|
||||
|
||||
//override fun close() {
|
||||
// //mongoClient.close()
|
||||
//}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import cn.tursom.core.isStatic
|
||||
import cn.tursom.core.isTransient
|
||||
import cn.tursom.core.uncheckedCast
|
||||
import cn.tursom.database.mongodb.annotation.Collection
|
||||
import cn.tursom.database.mongodb.annotation.Ignore
|
||||
import org.bson.BsonValue
|
||||
import org.bson.Document
|
||||
import org.bson.conversions.Bson
|
||||
import java.lang.reflect.Field
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.LinkedTransferQueue
|
||||
import java.util.concurrent.ThreadPoolExecutor
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.math.min
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
object MongoUtil {
|
||||
private val fieldNameCache = ConcurrentHashMap<Field, String>()
|
||||
private val mongoThreadId = AtomicInteger(0)
|
||||
val mongoExecutor = getThreadPool(min(4, Runtime.getRuntime().availableProcessors()))
|
||||
|
||||
fun collectionName(clazz: Class<*>): String {
|
||||
return clazz.getAnnotation(Collection::class.java)?.name ?: clazz.simpleName
|
||||
}
|
||||
|
||||
fun fieldName(field: Field): String {
|
||||
var fieldName = fieldNameCache[field]
|
||||
if (fieldName == null) {
|
||||
fieldName = field.getAnnotation(cn.tursom.database.mongodb.annotation.Field::class.java)?.name ?: field.name
|
||||
fieldNameCache[field] = fieldName!!
|
||||
}
|
||||
return fieldName
|
||||
}
|
||||
|
||||
fun fieldName(property: KProperty<*>): String {
|
||||
val javaField = property.javaField
|
||||
return if (javaField != null) fieldName(javaField) else property.name
|
||||
}
|
||||
|
||||
fun convertToBson(entity: Any): Document {
|
||||
return when (entity) {
|
||||
is Map<*, *> -> entity.convert()
|
||||
else -> {
|
||||
val bson = Document()
|
||||
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())
|
||||
}.forEach {
|
||||
injectValue(bson, it.get(entity) ?: return@forEach, it)
|
||||
}
|
||||
bson
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun injectValue(bson: Document, value: Any, field: Field) {
|
||||
when (value) {
|
||||
is Pair<*, *> -> bson[value.first?.toString() ?: return] = convertToBson(value.second ?: return)
|
||||
is Map.Entry<*, *> -> bson[value.key?.toString() ?: return] =
|
||||
convertToBson(value.value ?: return)
|
||||
else -> bson[fieldName(field)] = value.convert() ?: return
|
||||
}
|
||||
}
|
||||
|
||||
private fun Iterator<*>.convert(): List<*> {
|
||||
val list = ArrayList<Any?>()
|
||||
forEach {
|
||||
list.add(it.convert() ?: return@forEach)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
private fun Map<*, *>.convert(): Document {
|
||||
val doc = Document()
|
||||
forEach { any, u ->
|
||||
any ?: return@forEach
|
||||
doc[any.toString()] = u.convert() ?: return@forEach
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
private fun Any?.convert() = when (this) {
|
||||
null -> null
|
||||
is Enum<*> -> name
|
||||
is Boolean, is Number, is String, is Bson, is BsonValue, is ByteArray -> this
|
||||
is ShortArray -> asList()
|
||||
is IntArray -> asList()
|
||||
is LongArray -> asList()
|
||||
is FloatArray -> asList()
|
||||
is DoubleArray -> asList()
|
||||
is BooleanArray -> asList()
|
||||
is Map<*, *> -> this.convert()
|
||||
is Iterator<*> -> this.convert()
|
||||
is Iterable<*> -> this.iterator().convert()
|
||||
else -> convertToBson(this)
|
||||
}
|
||||
|
||||
private fun getThreadPool(nThreads: Int) = ThreadPoolExecutor(
|
||||
nThreads, nThreads,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
LinkedTransferQueue(),
|
||||
{
|
||||
val thread = Thread(it)
|
||||
thread.isDaemon = true
|
||||
thread.name = "mongo-worker-${mongoThreadId.incrementAndGet()}"
|
||||
thread
|
||||
},
|
||||
ThreadPoolExecutor.CallerRunsPolicy()
|
||||
)
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import com.mongodb.client.model.Projections
|
||||
import org.bson.conversions.Bson
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
object Projection {
|
||||
operator fun invoke(action: Projection.() -> Bson) = this.action()
|
||||
|
||||
fun <T, TExpression> computed(field: KProperty1<out T, *>, expression: TExpression): Bson =
|
||||
computed(MongoUtil.fieldName(field), expression)
|
||||
|
||||
fun <TExpression> computed(fieldName: String, expression: TExpression): Bson =
|
||||
Projections.computed(fieldName, expression)
|
||||
|
||||
fun <T> include(vararg fieldNames: KProperty1<out T, *>): Bson =
|
||||
Projections.include(fieldNames.map { MongoUtil.fieldName(it) })
|
||||
|
||||
fun <T> include(fieldNames: Collection<KProperty1<out T, *>>): Bson =
|
||||
Projections.include(fieldNames.map { MongoUtil.fieldName(it) })
|
||||
|
||||
fun include(vararg fieldNames: String): Bson = Projections.include(fieldNames.asList())
|
||||
fun include(fieldNames: List<String>): Bson = Projections.include(fieldNames)
|
||||
|
||||
fun <T> exclude(vararg fieldNames: KProperty1<out T, *>): Bson =
|
||||
Projections.exclude(fieldNames.map { MongoUtil.fieldName(it) })
|
||||
|
||||
fun <T> exclude(fieldNames: Collection<KProperty1<out T, *>>): Bson =
|
||||
Projections.exclude(fieldNames.map { MongoUtil.fieldName(it) })
|
||||
|
||||
fun exclude(vararg fieldNames: String): Bson = Projections.exclude(fieldNames.asList())
|
||||
fun exclude(fieldNames: List<String>): Bson = Projections.exclude(fieldNames)
|
||||
|
||||
fun excludeId(): Bson = Projections.excludeId()
|
||||
|
||||
fun <T> exclude(field: KProperty1<out T, *>): Bson = Projections.elemMatch(MongoUtil.fieldName(field))
|
||||
fun <T> exclude(field: KProperty1<out T, *>, filter: Bson): Bson =
|
||||
Projections.elemMatch(MongoUtil.fieldName(field), filter)
|
||||
|
||||
fun elemMatch(fieldName: String): Bson = Projections.elemMatch(fieldName)
|
||||
fun elemMatch(fieldName: String, filter: Bson): Bson = Projections.elemMatch(fieldName, filter)
|
||||
|
||||
fun <T> metaTextScore(field: KProperty1<out T, *>): Bson = Projections.metaTextScore(MongoUtil.fieldName(field))
|
||||
fun metaTextScore(fieldName: String): Bson = Projections.metaTextScore(fieldName)
|
||||
|
||||
fun <T> slice(field: KProperty1<out T, *>, limit: Int): Bson = Projections.slice(MongoUtil.fieldName(field), limit)
|
||||
fun <T> slice(field: KProperty1<out T, *>, skip: Int, limit: Int): Bson =
|
||||
Projections.slice(MongoUtil.fieldName(field), skip, limit)
|
||||
|
||||
fun slice(fieldName: String, limit: Int): Bson = Projections.slice(fieldName, limit)
|
||||
fun slice(fieldName: String, skip: Int, limit: Int): Bson = Projections.slice(fieldName, skip, limit)
|
||||
|
||||
fun fields(vararg projections: Bson): Bson = Projections.fields(projections.asList())
|
||||
fun fields(projections: List<Bson>): Bson = Projections.fields(projections)
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import com.mongodb.client.model.Sorts
|
||||
import org.bson.conversions.Bson
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
object Sort {
|
||||
operator fun invoke(action: Sort.() -> Bson) = this.action()
|
||||
|
||||
fun <T> ascending(vararg fieldNames: KProperty1<out T, *>): Bson =
|
||||
Sorts.ascending(fieldNames.map { MongoUtil.fieldName(it) })
|
||||
|
||||
fun <T> ascending(fieldNames: Collection<KProperty1<out T, *>>): Bson =
|
||||
Sorts.ascending(fieldNames.map { MongoUtil.fieldName(it) })
|
||||
|
||||
fun ascending(vararg fieldNames: String): Bson = Sorts.ascending(fieldNames.asList())
|
||||
fun ascending(fieldNames: List<String>): Bson = Sorts.ascending(fieldNames)
|
||||
fun <T> descending(vararg fieldNames: KProperty1<out T, *>): Bson =
|
||||
Sorts.descending(fieldNames.map { MongoUtil.fieldName(it) })
|
||||
|
||||
fun <T> descending(fieldNames: Collection<KProperty1<out T, *>>): Bson =
|
||||
Sorts.descending(fieldNames.map { MongoUtil.fieldName(it) })
|
||||
|
||||
fun descending(vararg fieldNames: String): Bson = Sorts.descending(fieldNames.asList())
|
||||
fun descending(fieldNames: List<String>): Bson = Sorts.descending(fieldNames)
|
||||
fun <T> KProperty1<out T, *>.metaTextScore(): Bson = Sorts.metaTextScore(MongoUtil.fieldName(this))
|
||||
fun metaTextScore(fieldName: String): Bson = Sorts.metaTextScore(fieldName)
|
||||
fun orderBy(vararg sorts: Bson): Bson = Sorts.orderBy(sorts.asList())
|
||||
fun orderBy(sorts: List<Bson>): Bson = Sorts.orderBy(sorts)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import com.mongodb.client.model.Updates
|
||||
import org.bson.conversions.Bson
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
object Update : MongoName {
|
||||
operator fun invoke(action: Update.() -> Bson) = this.action()
|
||||
|
||||
fun and(vararg update: Bson): Bson = Updates.combine(*update)
|
||||
fun or(vararg update: Bson): Bson = Updates.combine(*update)
|
||||
infix fun Bson.and(update: Bson): Bson = Updates.combine(this, update)
|
||||
infix fun Bson.and(update: Update.() -> Bson): Bson = and(Update.update())
|
||||
fun combine(vararg updates: Bson): Bson = Updates.combine(*updates)
|
||||
|
||||
|
||||
infix fun String.set(value: Any): Bson = when (value) {
|
||||
is String, is Number, is Boolean -> Updates.set(this, value)
|
||||
else -> Updates.set(this, MongoUtil.convertToBson(value))
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.set(value: Any): Bson = MongoUtil.fieldName(this) set value
|
||||
|
||||
fun KProperty<*>.unset(): Bson = Updates.unset(MongoUtil.fieldName(this))
|
||||
fun setOnInsert(value: Bson): Bson = Updates.setOnInsert(value)
|
||||
fun setOnInsert(value: Any): Bson = Updates.setOnInsert(MongoUtil.convertToBson(value))
|
||||
infix fun KProperty<*>.setOnInsert(value: Any): Bson = Updates.setOnInsert(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.rename(value: String): Bson = Updates.rename(MongoUtil.fieldName(this), value)
|
||||
infix fun String.rename(value: String): Bson = Updates.rename(this, value)
|
||||
infix fun KProperty<*>.inc(value: Number): Bson = Updates.inc(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.mul(value: Number): Bson = Updates.mul(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.min(value: Any): Bson = Updates.min(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.max(value: Any): Bson = Updates.max(MongoUtil.fieldName(this), value)
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import com.mongodb.client.model.Filters
|
||||
import com.mongodb.client.model.TextSearchOptions
|
||||
import org.bson.BsonType
|
||||
import org.bson.conversions.Bson
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("unused")
|
||||
object Where : MongoName {
|
||||
inline operator fun invoke(action: Where.() -> Bson) = this.action()
|
||||
|
||||
infix fun String.eq(value: Any): Bson = Filters.eq(this, value)
|
||||
infix fun String.ne(value: Any): Bson = Filters.ne(this, value)
|
||||
infix fun String.gt(value: Any): Bson = Filters.gt(this, value)
|
||||
infix fun String.lt(value: Any): Bson = Filters.lt(this, value)
|
||||
infix fun String.gte(value: Any): Bson = Filters.gte(this, value)
|
||||
infix fun String.lte(value: Any): Bson = Filters.lte(this, value)
|
||||
fun String.`in`(vararg value: Any): Bson = Filters.`in`(this, *value)
|
||||
fun String.nin(vararg value: Any): Bson = Filters.nin(this, *value)
|
||||
fun String.all(vararg value: Any): Bson = Filters.all(this, *value)
|
||||
infix fun String.all(value: Iterable<Any>): Bson = Filters.all(this, value)
|
||||
fun String.exists(exists: Boolean = true): Bson = Filters.exists(this, exists)
|
||||
|
||||
infix fun String.type(type: String): Bson = Filters.type(this, type)
|
||||
infix fun String.type(type: BsonType): Bson = Filters.type(this, type)
|
||||
fun String.mod(divisor: Long, remainder: Long): Bson = Filters.mod(this, divisor, remainder)
|
||||
infix fun String.regex(regex: String): Bson = Filters.regex(this, regex)
|
||||
infix fun String.regex(regex: Regex): Bson = Filters.regex(this, regex.pattern)
|
||||
fun String.regex(regex: String, option: String): Bson = Filters.regex(this, regex, option)
|
||||
fun String.regex(regex: Regex, option: String): Bson = Filters.regex(this, regex.pattern, option)
|
||||
|
||||
infix fun String.elemMatch(where: Bson): Bson = Filters.elemMatch(this, where)
|
||||
infix fun String.elemMatch(where: Where.() -> Bson): Bson = Filters.elemMatch(this, Where.where())
|
||||
|
||||
infix fun KProperty<*>.eq(value: Any): Bson = Filters.eq(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.ne(value: Any): Bson = Filters.ne(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.gt(value: Any): Bson = Filters.gt(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.lt(value: Any): Bson = Filters.lt(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.gte(value: Any): Bson = Filters.gte(MongoUtil.fieldName(this), value)
|
||||
infix fun KProperty<*>.lte(value: Any): Bson = Filters.lte(MongoUtil.fieldName(this), value)
|
||||
fun KProperty<*>.`in`(vararg value: Any): Bson = Filters.`in`(MongoUtil.fieldName(this), *value)
|
||||
fun KProperty<*>.nin(vararg value: Any): Bson = Filters.nin(MongoUtil.fieldName(this), *value)
|
||||
fun KProperty<*>.all(vararg value: Any): Bson = Filters.all(MongoUtil.fieldName(this), *value)
|
||||
infix fun KProperty<*>.all(value: Iterable<Any>): Bson = Filters.all(MongoUtil.fieldName(this), value)
|
||||
fun KProperty<*>.exists(exists: Boolean = true): Bson = Filters.exists(MongoUtil.fieldName(this), exists)
|
||||
|
||||
infix fun KProperty<*>.type(type: String): Bson = Filters.type(MongoUtil.fieldName(this), type)
|
||||
infix fun KProperty<*>.type(type: BsonType): Bson = Filters.type(MongoUtil.fieldName(this), type)
|
||||
fun KProperty<*>.mod(divisor: Long, remainder: Long): Bson =
|
||||
Filters.mod(MongoUtil.fieldName(this), divisor, remainder)
|
||||
|
||||
infix fun KProperty<*>.regex(regex: String): Bson = Filters.regex(MongoUtil.fieldName(this), regex)
|
||||
infix fun KProperty<*>.regex(regex: Regex): Bson = Filters.regex(MongoUtil.fieldName(this), regex.pattern)
|
||||
fun KProperty<*>.regex(regex: String, option: String): Bson = Filters.regex(MongoUtil.fieldName(this), regex, option)
|
||||
fun KProperty<*>.regex(regex: Regex, option: String): Bson =
|
||||
Filters.regex(MongoUtil.fieldName(this), regex.pattern, option)
|
||||
|
||||
infix fun KProperty<*>.elemMatch(where: Bson): Bson = MongoUtil.fieldName(this) elemMatch where
|
||||
infix fun KProperty<*>.elemMatch(where: Where.() -> Bson): Bson = MongoUtil.fieldName(this) elemMatch where
|
||||
|
||||
fun text(search: String): Bson = Filters.text(search)
|
||||
fun text(search: String, option: TextSearchOptions): Bson = Filters.text(search, option)
|
||||
fun where(javaScriptExpression: String): Bson = Filters.where(javaScriptExpression)
|
||||
fun expr(expression: Any): Bson = Filters.expr(expression)
|
||||
|
||||
fun and(vararg bson: Bson): Bson = Filters.and(*bson)
|
||||
infix fun Bson.and(bson: Bson): Bson = Filters.and(this, bson)
|
||||
fun or(vararg bson: Bson): Bson = Filters.or(*bson)
|
||||
infix fun Bson.or(bson: Bson): Bson = Filters.or(this, bson)
|
||||
fun nor(vararg bson: Bson): Bson = Filters.or(*bson)
|
||||
infix fun Bson.nor(bson: Bson): Bson = Filters.nor(this, bson)
|
||||
|
||||
operator fun Bson.not(): Bson = Filters.not(this)
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package cn.tursom.database.mongodb.annotation
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class Collection(val name: String)
|
||||
|
@ -0,0 +1,6 @@
|
||||
package cn.tursom.database.mongodb.annotation
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Field(val name: String)
|
||||
|
@ -0,0 +1,5 @@
|
||||
package cn.tursom.database.mongodb.annotation
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class Ignore(val name: String)
|
@ -0,0 +1,95 @@
|
||||
package cn.tursom.database.mongodb.operator
|
||||
|
||||
import cn.tursom.log.Slf4j
|
||||
import cn.tursom.log.impl.Slf4jImpl
|
||||
import com.mongodb.client.model.InsertManyOptions
|
||||
import com.mongodb.client.model.InsertOneOptions
|
||||
import com.mongodb.client.result.InsertOneResult
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
|
||||
import org.bson.Document
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class AsyncInsertMongoOperator<T : Any>(
|
||||
private val mongoOperator: MongoOperator<T>,
|
||||
) : MongoOperator<T> by mongoOperator {
|
||||
companion object : Slf4j by Slf4jImpl() {
|
||||
private val acknowledged = InsertOneResult.acknowledged(null)
|
||||
}
|
||||
|
||||
private val wroteAtomic = AtomicInteger(0)
|
||||
private val documentChannel: Channel<Document> = Channel(1024 * 256)
|
||||
|
||||
val wrote get() = wroteAtomic.get()
|
||||
|
||||
private val job = GlobalScope.launch {
|
||||
repeat(32) {
|
||||
launch {
|
||||
var list = ArrayList<Document>()
|
||||
var lastSave = System.currentTimeMillis()
|
||||
suspend fun save() {
|
||||
if (list.isEmpty()) return
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("begin to save document: {}", list)
|
||||
}
|
||||
var saveDocument = mongoOperator.saveDocument(list, InsertManyOptions().ordered(false))
|
||||
var retryTimes = 0
|
||||
while (saveDocument?.wasAcknowledged() != true && retryTimes < 3) {
|
||||
retryTimes++
|
||||
delay(100)
|
||||
saveDocument = mongoOperator.saveDocument(list)
|
||||
}
|
||||
list = ArrayList()
|
||||
lastSave = System.currentTimeMillis()
|
||||
saveDocument ?: return
|
||||
wroteAtomic.addAndGet(saveDocument.insertedIds.size)
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("save result: {}", saveDocument)
|
||||
}
|
||||
}
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
while (!documentChannel.isClosedForReceive) {
|
||||
try {
|
||||
list.add(withTimeout(100) { documentChannel.receive() })
|
||||
} catch (e: TimeoutCancellationException) {
|
||||
} catch (e: ClosedReceiveChannelException) {
|
||||
break
|
||||
}
|
||||
if (list.size >= 1024 || System.currentTimeMillis() - lastSave > 100) {
|
||||
save()
|
||||
}
|
||||
}
|
||||
if (list.size > 0) {
|
||||
save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun save(entity: T, options: InsertOneOptions): Boolean {
|
||||
//insertOne(entity, options)
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("save object: {}, options: {}, result: {}", entity, options, insertOne(entity, options))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun saveDocument(document: Document, options: InsertOneOptions): InsertOneResult? {
|
||||
documentChannel.send(document)
|
||||
return acknowledged
|
||||
}
|
||||
|
||||
override suspend fun insertOne(entity: T, options: InsertOneOptions): InsertOneResult? {
|
||||
documentChannel.send(convertToBson(entity))
|
||||
return acknowledged
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
documentChannel.close()
|
||||
}
|
||||
|
||||
suspend fun join() {
|
||||
job.join()
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package cn.tursom.database.mongodb.operator
|
||||
|
||||
import com.mongodb.client.model.InsertManyOptions
|
||||
import com.mongodb.client.model.InsertOneOptions
|
||||
import com.mongodb.client.result.InsertOneResult
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
|
||||
import org.bson.Document
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class CachedInsertMongoOperator<T : Any>(
|
||||
private val mongoOperator: MongoOperator<T>,
|
||||
private val ordered: Boolean = true,
|
||||
private val maxSaveCount: Int = 4096,
|
||||
private val maxDelayTimeMS: Long = 100,
|
||||
) : MongoOperator<T> by mongoOperator {
|
||||
companion object {
|
||||
private val unacknowledged = InsertOneResult.unacknowledged()
|
||||
}
|
||||
|
||||
private val documentChannel: Channel<Pair<Channel<InsertOneResult?>, Document>> = Channel(1024 * 256)
|
||||
private val wroteAtomic = AtomicInteger(0)
|
||||
private val received = AtomicInteger(0)
|
||||
val wrote get() = wroteAtomic.get()
|
||||
|
||||
private val job = GlobalScope.launch {
|
||||
repeat(32) {
|
||||
launch {
|
||||
var list = ArrayList<Pair<Channel<InsertOneResult?>, Document>>()
|
||||
var lastSave = System.currentTimeMillis()
|
||||
suspend fun save() {
|
||||
var insertManyResult = saveDocument(
|
||||
list.map(Pair<Channel<InsertOneResult?>, Document>::second),
|
||||
InsertManyOptions().ordered(ordered)
|
||||
)
|
||||
var retryTimes = 0
|
||||
while (insertManyResult?.wasAcknowledged() != true && retryTimes < 3) {
|
||||
retryTimes++
|
||||
delay(100)
|
||||
insertManyResult = saveDocument(
|
||||
list.map(Pair<Channel<InsertOneResult?>, Document>::second),
|
||||
InsertManyOptions().ordered(ordered)
|
||||
)
|
||||
}
|
||||
if (insertManyResult?.wasAcknowledged() == true) {
|
||||
wroteAtomic.addAndGet(insertManyResult.insertedIds.size)
|
||||
list.forEachIndexed { index, (channel) ->
|
||||
channel.send(InsertOneResult.acknowledged(insertManyResult.insertedIds[index]))
|
||||
}
|
||||
} else {
|
||||
list.forEach { (channel) ->
|
||||
channel.send(unacknowledged)
|
||||
}
|
||||
}
|
||||
list = ArrayList()
|
||||
lastSave = System.currentTimeMillis()
|
||||
}
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
while (!documentChannel.isClosedForReceive) {
|
||||
try {
|
||||
list.add(documentChannel.poll() ?: withTimeout(maxDelayTimeMS) { documentChannel.receive() })
|
||||
received.incrementAndGet()
|
||||
} catch (e: TimeoutCancellationException) {
|
||||
} catch (e: ClosedReceiveChannelException) {
|
||||
//println("closed")
|
||||
break
|
||||
}
|
||||
if (list.size >= maxSaveCount || System.currentTimeMillis() - lastSave > maxDelayTimeMS) {
|
||||
save()
|
||||
}
|
||||
}
|
||||
if (list.size > 0) {
|
||||
save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun save(entity: T, options: InsertOneOptions): Boolean {
|
||||
return insertOne(entity, options)?.wasAcknowledged() ?: false
|
||||
}
|
||||
|
||||
override suspend fun saveDocument(document: Document, options: InsertOneOptions): InsertOneResult? {
|
||||
val channel = Channel<InsertOneResult?>()
|
||||
documentChannel.send(channel to document)
|
||||
return channel.receive()
|
||||
}
|
||||
|
||||
override suspend fun insertOne(entity: T, options: InsertOneOptions): InsertOneResult? {
|
||||
val channel = Channel<InsertOneResult?>()
|
||||
documentChannel.send(channel to convertToBson(entity))
|
||||
return channel.receive()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
documentChannel.close()
|
||||
mongoOperator.close()
|
||||
}
|
||||
|
||||
suspend fun join() {
|
||||
job.join()
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package cn.tursom.database.mongodb.operator
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
@Suppress("FunctionName", "unused")
|
||||
fun <T : Any> DynamicCachedInsertMongoOperator(
|
||||
mongoOperator: MongoOperator<T>,
|
||||
executorService: ScheduledExecutorService,
|
||||
loadCheckDelay: Long = 100,
|
||||
loadCheckDelayTimeUnit: TimeUnit = TimeUnit.MILLISECONDS,
|
||||
ordered: Boolean = true,
|
||||
maxSaveCount: Int = 4096,
|
||||
maxDelayTimeMS: Long = 100,
|
||||
mainTransLoad: Int = 10,
|
||||
spareTransLoad: Int = 50,
|
||||
transWaitCount: Int = 10,
|
||||
) = DynamicInsertMongoOperator(
|
||||
mongoOperator,
|
||||
CachedInsertMongoOperator(mongoOperator, ordered, maxSaveCount, maxDelayTimeMS),
|
||||
executorService,
|
||||
loadCheckDelay,
|
||||
loadCheckDelayTimeUnit,
|
||||
object : (DynamicInsertMongoOperator.WorkType, Int) -> DynamicInsertMongoOperator.WorkType {
|
||||
private var workLoop = AtomicInteger(0)
|
||||
override fun invoke(workType: DynamicInsertMongoOperator.WorkType, load: Int): DynamicInsertMongoOperator.WorkType {
|
||||
when (workType) {
|
||||
DynamicInsertMongoOperator.WorkType.MAIN -> if (load > spareTransLoad) {
|
||||
if (workLoop.incrementAndGet() > transWaitCount) {
|
||||
workLoop.set(0)
|
||||
return DynamicInsertMongoOperator.WorkType.SPARE
|
||||
}
|
||||
}
|
||||
DynamicInsertMongoOperator.WorkType.SPARE -> if (load < mainTransLoad) {
|
||||
if (workLoop.incrementAndGet() > transWaitCount) {
|
||||
workLoop.set(0)
|
||||
return DynamicInsertMongoOperator.WorkType.MAIN
|
||||
}
|
||||
}
|
||||
}
|
||||
return workType
|
||||
}
|
||||
}
|
||||
)
|
@ -0,0 +1,62 @@
|
||||
package cn.tursom.database.mongodb.operator
|
||||
|
||||
import com.mongodb.client.model.InsertOneOptions
|
||||
import com.mongodb.client.result.InsertOneResult
|
||||
import org.bson.Document
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class DynamicInsertMongoOperator<T : Any>(
|
||||
private val mainMongoOperator: MongoOperator<T>,
|
||||
private val spareMongoOperator: MongoOperator<T>,
|
||||
private val executorService: ScheduledExecutorService,
|
||||
loadCheckDelay: Long = 100,
|
||||
loadCheckDelayTimeUnit: TimeUnit = TimeUnit.MILLISECONDS,
|
||||
private val workTypeCheck: (workType: WorkType, load: Int) -> WorkType,
|
||||
) : MongoOperator<T> by mainMongoOperator {
|
||||
enum class WorkType {
|
||||
MAIN, SPARE
|
||||
}
|
||||
|
||||
var workType = WorkType.MAIN
|
||||
private val workLoad = AtomicInteger(0)
|
||||
|
||||
init {
|
||||
executorService.scheduleWithFixedDelay({
|
||||
workType = workTypeCheck(workType, workLoad.getAndSet(0))
|
||||
}, loadCheckDelay, loadCheckDelay, loadCheckDelayTimeUnit)
|
||||
}
|
||||
|
||||
private val workMongoOperator
|
||||
get() = when (workType) {
|
||||
WorkType.MAIN -> mainMongoOperator
|
||||
WorkType.SPARE -> spareMongoOperator
|
||||
}
|
||||
|
||||
override suspend fun save(entity: T, options: InsertOneOptions): Boolean {
|
||||
workLoad.incrementAndGet()
|
||||
return workMongoOperator.save(entity, options)
|
||||
}
|
||||
|
||||
override suspend fun saveDocument(document: Document, options: InsertOneOptions): InsertOneResult? {
|
||||
workLoad.incrementAndGet()
|
||||
return workMongoOperator.saveDocument(document, options)
|
||||
}
|
||||
|
||||
override suspend fun insertOne(entity: T, options: InsertOneOptions): InsertOneResult? {
|
||||
workLoad.incrementAndGet()
|
||||
return workMongoOperator.insertOne(entity, options)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
try {
|
||||
mainMongoOperator.close()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
try {
|
||||
spareMongoOperator.close()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
package cn.tursom.database.mongodb.operator
|
||||
|
||||
import cn.tursom.core.datastruct.AsyncIterator
|
||||
import cn.tursom.database.mongodb.BsonFactory
|
||||
import cn.tursom.database.mongodb.IndexBuilder
|
||||
import cn.tursom.database.mongodb.Update
|
||||
import cn.tursom.database.mongodb.Where
|
||||
import com.mongodb.client.model.*
|
||||
import com.mongodb.client.result.InsertManyResult
|
||||
import com.mongodb.client.result.InsertOneResult
|
||||
import com.mongodb.client.result.UpdateResult
|
||||
import org.bson.Document
|
||||
import org.bson.conversions.Bson
|
||||
import java.io.Closeable
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
interface MongoOperator<T : Any> : BsonFactory<T>, Closeable {
|
||||
suspend fun save(entity: T, options: InsertOneOptions = InsertOneOptions()): Boolean {
|
||||
return insertOne(entity, options)?.wasAcknowledged() ?: false
|
||||
}
|
||||
|
||||
suspend fun save(entities: Collection<T>, options: InsertManyOptions = InsertManyOptions()) {
|
||||
insertMany(entities, options)
|
||||
}
|
||||
|
||||
suspend fun saveDocument(document: Document, options: InsertOneOptions = InsertOneOptions()): InsertOneResult?
|
||||
|
||||
suspend fun saveDocument(
|
||||
documents: List<Document>,
|
||||
options: InsertManyOptions = InsertManyOptions()
|
||||
): InsertManyResult?
|
||||
|
||||
suspend fun insertOne(entity: T, options: InsertOneOptions = InsertOneOptions()): InsertOneResult? {
|
||||
return saveDocument(convertToBson(entity))
|
||||
}
|
||||
|
||||
suspend fun insertMany(entities: Collection<T>, options: InsertManyOptions = InsertManyOptions()): InsertManyResult? {
|
||||
return saveDocument(entities.map { convertToBson(it) }, options)
|
||||
}
|
||||
|
||||
suspend fun updateMulti(update: Bson, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult?
|
||||
|
||||
suspend fun update(update: Bson, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult?
|
||||
|
||||
suspend fun update(entity: T, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult? {
|
||||
return update(convertToBson(entity), where, options)
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
suspend fun upsert(entity: T, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult? {
|
||||
return update(entity, where, options.upsert(true))
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
suspend fun upsert(update: Bson, where: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult? {
|
||||
return update(update, where, options.upsert(true))
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
suspend fun upsert(
|
||||
update: Update.() -> Bson,
|
||||
where: Where.() -> Bson,
|
||||
options: UpdateOptions = UpdateOptions(),
|
||||
): UpdateResult? {
|
||||
return upsert(Update.update(), Where.where(), options)
|
||||
}
|
||||
|
||||
suspend fun add(
|
||||
field: KProperty1<T, Number?>,
|
||||
value: Number,
|
||||
where: Bson,
|
||||
options: UpdateOptions = UpdateOptions(),
|
||||
): UpdateResult? {
|
||||
return upsert(
|
||||
Update { field inc value },
|
||||
where, options
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun inc(field: KProperty1<T, Number?>, where: Bson): UpdateResult? {
|
||||
return add(field, 1, where)
|
||||
}
|
||||
|
||||
suspend fun getOne(where: Bson? = null): T?
|
||||
|
||||
suspend fun list(where: Bson? = null, skip: Int = 0, limit: Int = 0): List<T>
|
||||
|
||||
suspend fun listDocument(where: Bson? = null, skip: Int = 0, limit: Int = 0): List<Document>
|
||||
|
||||
fun get(where: Bson? = null, bufSize: Int = 32): AsyncIterator<T>
|
||||
|
||||
fun getDocument(where: Bson? = null, bufSize: Int = 32, skip: Int = 0, limit: Int = 0): AsyncIterator<Document>
|
||||
|
||||
fun aggregate(vararg pipeline: Bson, bufSize: Int = 32): AsyncIterator<T>
|
||||
|
||||
fun delete(entity: T, options: DeleteOptions = DeleteOptions())
|
||||
|
||||
suspend fun saveIfNotExists(entity: T) {
|
||||
val document = convertToBson(entity)
|
||||
upsert(document, document)
|
||||
}
|
||||
|
||||
suspend fun count(): Long
|
||||
suspend fun count(where: Bson): Long
|
||||
|
||||
suspend fun createIndexSuspend(key: Bson, indexOptions: IndexOptions = IndexOptions()): String?
|
||||
|
||||
suspend fun createIndexSuspend(indexBuilder: IndexBuilder.() -> Unit): String? {
|
||||
val builder = IndexBuilder()
|
||||
builder.indexBuilder()
|
||||
return createIndexSuspend(builder.indexDocument, builder.indexOption)
|
||||
}
|
||||
|
||||
override fun close() {}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
package cn.tursom.database.mongodb.operator
|
||||
|
||||
import cn.tursom.core.datastruct.AsyncIterator
|
||||
import cn.tursom.database.mongodb.*
|
||||
import cn.tursom.database.mongodb.subscriber.*
|
||||
import cn.tursom.log.Slf4j
|
||||
import cn.tursom.log.impl.Slf4jImpl
|
||||
import com.mongodb.client.model.*
|
||||
import com.mongodb.client.result.InsertManyResult
|
||||
import com.mongodb.client.result.InsertOneResult
|
||||
import com.mongodb.client.result.UpdateResult
|
||||
import com.mongodb.reactivestreams.client.MongoCollection
|
||||
import com.mongodb.reactivestreams.client.MongoDatabase
|
||||
import org.bson.Document
|
||||
import org.bson.conversions.Bson
|
||||
import org.reactivestreams.Publisher
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class MongoOperatorImpl<T : Any>(
|
||||
@Suppress("MemberVisibilityCanBePrivate") val collection: MongoCollection<Document>,
|
||||
clazz: Class<T>,
|
||||
private val subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : MongoOperator<T>, MongoCollection<Document> by collection, BsonFactoryImpl<T>(clazz) {
|
||||
companion object : Slf4j by Slf4jImpl()
|
||||
|
||||
constructor(
|
||||
clazz: Class<T>,
|
||||
db: MongoDatabase,
|
||||
subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : this(db.getCollection(MongoUtil.collectionName(clazz)), clazz, subscribeExecutor)
|
||||
|
||||
override suspend fun save(entity: T, options: InsertOneOptions): Boolean {
|
||||
return insertOne(entity, options)?.wasAcknowledged() ?: false
|
||||
}
|
||||
|
||||
override suspend fun save(entities: Collection<T>, options: InsertManyOptions) {
|
||||
insertMany(entities, options)
|
||||
}
|
||||
|
||||
override suspend fun saveDocument(document: Document, options: InsertOneOptions): InsertOneResult? {
|
||||
return suspendCoroutine { cont ->
|
||||
insertOne(document, options).subscribe(SuspendInsertOneSubscriber(cont, subscribeExecutor = subscribeExecutor))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun saveDocument(documents: List<Document>, options: InsertManyOptions): InsertManyResult? {
|
||||
val publisher = collection.insertMany(documents, options)
|
||||
return suspendCoroutine { cont ->
|
||||
publisher.subscribe(
|
||||
SuspendInsertOneSubscriber(
|
||||
cont,
|
||||
documents.size.toLong(),
|
||||
subscribeExecutor = subscribeExecutor
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun insertOne(entity: T, options: InsertOneOptions): InsertOneResult? {
|
||||
return saveDocument(convertToBson(entity))
|
||||
}
|
||||
|
||||
override suspend fun insertMany(entities: Collection<T>, options: InsertManyOptions): InsertManyResult? {
|
||||
return saveDocument(entities.map { convertToBson(it) }, options)
|
||||
//val publisher = collection.insertMany(entities.map { convertToBson(it) }, options)
|
||||
//return suspendCoroutine { cont ->
|
||||
// publisher.subscribe(SuspendInsertListSubscriber(cont, entities.size.toLong(), subscribeExecutor = subscribeExecutor))
|
||||
//}
|
||||
}
|
||||
|
||||
override suspend fun updateMulti(update: Bson, where: Bson, options: UpdateOptions): UpdateResult? {
|
||||
val publisher = collection.updateMany(where, update, options)
|
||||
return suspendCoroutine { cont ->
|
||||
publisher.subscribe(SuspendInsertOneSubscriber(cont, subscribeExecutor = subscribeExecutor))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun update(update: Bson, where: Bson, options: UpdateOptions): UpdateResult? {
|
||||
val publisher = collection.updateOne(where, update, options)
|
||||
return suspendCoroutine { cont ->
|
||||
publisher.subscribe(SuspendInsertOneSubscriber(cont, subscribeExecutor = subscribeExecutor))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun update(entity: T, where: Bson, options: UpdateOptions): UpdateResult? {
|
||||
return update(convertToBson(entity), where, options)
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
override suspend fun upsert(entity: T, where: Bson, options: UpdateOptions): UpdateResult? {
|
||||
return update(entity, where, options.upsert(true))
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
override suspend fun upsert(update: Bson, where: Bson, options: UpdateOptions): UpdateResult? {
|
||||
return update(update, where, options.upsert(true))
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
override suspend fun upsert(
|
||||
update: Update.() -> Bson,
|
||||
where: Where.() -> Bson,
|
||||
options: UpdateOptions,
|
||||
): UpdateResult? {
|
||||
return upsert(Update.update(), Where.where(), options)
|
||||
}
|
||||
|
||||
override suspend fun add(
|
||||
field: KProperty1<T, Number?>,
|
||||
value: Number,
|
||||
where: Bson,
|
||||
options: UpdateOptions,
|
||||
): UpdateResult? {
|
||||
return upsert(
|
||||
Update { field inc value },
|
||||
where, options
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun inc(field: KProperty1<T, Number?>, where: Bson): UpdateResult? {
|
||||
return add(field, 1, where)
|
||||
}
|
||||
|
||||
override suspend fun getOne(where: Bson?): T? {
|
||||
val publisher = if (where == null) find() else find(where)
|
||||
return suspendCoroutine { cont ->
|
||||
publisher.subscribe(SuspendOneSubscriber(this, cont, subscribeExecutor = subscribeExecutor))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun list(where: Bson?, skip: Int, limit: Int): List<T> {
|
||||
val publisher = if (where == null) find() else find(where)
|
||||
publisher.skip(skip)
|
||||
if (limit > 0) {
|
||||
publisher.limit(limit)
|
||||
}
|
||||
return suspendCoroutine { cont ->
|
||||
publisher.subscribe(
|
||||
SuspendListSubscriber(
|
||||
this,
|
||||
cont,
|
||||
if (limit > 0) limit.toLong() else Long.MAX_VALUE,
|
||||
subscribeExecutor = subscribeExecutor
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun listDocument(where: Bson?, skip: Int, limit: Int): List<Document> {
|
||||
println("skip: $skip, limit: $limit")
|
||||
val publisher = if (where == null) find() else find(where)
|
||||
publisher.skip(skip)
|
||||
if (limit > 0) {
|
||||
publisher.limit(limit)
|
||||
}
|
||||
return suspendCoroutine { cont ->
|
||||
publisher.subscribe(
|
||||
SuspendListDocumentSubscriber(
|
||||
cont,
|
||||
if (limit > 0) limit.toLong() else Long.MAX_VALUE,
|
||||
subscribeExecutor = subscribeExecutor
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(where: Bson?, bufSize: Int): AsyncIterator<T> {
|
||||
val find = if (where == null) find() else find(where)
|
||||
return iterator(find, bufSize)
|
||||
}
|
||||
|
||||
override fun getDocument(where: Bson?, bufSize: Int, skip: Int, limit: Int): AsyncIterator<Document> {
|
||||
val find = if (where == null) find() else find(where)
|
||||
find.skip(skip)
|
||||
if (limit > 0) {
|
||||
find.limit(limit)
|
||||
}
|
||||
return documentIterator(find, bufSize)
|
||||
}
|
||||
|
||||
override fun aggregate(vararg pipeline: Bson, bufSize: Int): AsyncIterator<T> =
|
||||
iterator(aggregate(pipeline.asList()), bufSize)
|
||||
|
||||
private fun iterator(publisher: Publisher<Document>, bufSize: Int = 32): AsyncIterator<T> {
|
||||
val subscriber = AsyncIteratorSubscriber(
|
||||
this,
|
||||
bufSize,
|
||||
subscribeExecutor = subscribeExecutor
|
||||
)
|
||||
publisher.subscribe(subscriber)
|
||||
return subscriber
|
||||
}
|
||||
|
||||
private fun documentIterator(publisher: Publisher<Document>, bufSize: Int = 32): AsyncIterator<Document> {
|
||||
val subscriber = AsyncDocumentIteratorSubscriber(bufSize, subscribeExecutor = subscribeExecutor)
|
||||
publisher.subscribe(subscriber)
|
||||
return subscriber
|
||||
}
|
||||
|
||||
override fun delete(entity: T, options: DeleteOptions) {
|
||||
deleteOne(convertToBson(entity), options)
|
||||
}
|
||||
|
||||
override suspend fun saveIfNotExists(entity: T) {
|
||||
val document = convertToBson(entity)
|
||||
upsert(document, document)
|
||||
}
|
||||
|
||||
override suspend fun count(): Long {
|
||||
val publisher = countDocuments()
|
||||
return suspendCoroutine { cont ->
|
||||
publisher.subscribe(SuspendInsertOneSubscriber(cont, subscribeExecutor = subscribeExecutor))
|
||||
} ?: 0L
|
||||
}
|
||||
|
||||
override suspend fun count(where: Bson): Long {
|
||||
val publisher = countDocuments(where)
|
||||
return suspendCoroutine { cont ->
|
||||
publisher.subscribe(SuspendInsertOneSubscriber(cont, subscribeExecutor = subscribeExecutor))
|
||||
} ?: 0L
|
||||
}
|
||||
|
||||
override suspend fun createIndexSuspend(key: Bson, indexOptions: IndexOptions): String? {
|
||||
return suspendCoroutine { cont ->
|
||||
createIndex(key, indexOptions).subscribe(SuspendInsertOneSubscriber(cont, subscribeExecutor = subscribeExecutor))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun createIndexSuspend(indexBuilder: IndexBuilder.() -> Unit): String? {
|
||||
val builder = IndexBuilder()
|
||||
builder.indexBuilder()
|
||||
return createIndexSuspend(builder.indexDocument, builder.indexOption)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "MongoOperator(collection=$collection, clazz=$clazz)"
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import org.bson.Document
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
abstract class AbstractDocumentSubscriber(
|
||||
val size: Long = 1,
|
||||
val subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : Subscriber<Document> {
|
||||
abstract fun next(t: Document)
|
||||
|
||||
private var requestRemain = 0L
|
||||
protected var requested = 0
|
||||
protected lateinit var s: Subscription
|
||||
protected var compete = false
|
||||
|
||||
override fun onComplete() {
|
||||
compete = true
|
||||
}
|
||||
|
||||
override fun onSubscribe(s: Subscription) {
|
||||
this.s = s
|
||||
requestRemain = size - requested
|
||||
subscribeExecutor.execute {
|
||||
s.request(requestRemain)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNext(t: Document) {
|
||||
next(t)
|
||||
requestRemain--
|
||||
requested++
|
||||
if (requestRemain > 0) return
|
||||
if (size - requested > 0) {
|
||||
s.request(size - requested)
|
||||
} else {
|
||||
onComplete()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.database.mongodb.BsonFactory
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import org.bson.Document
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
abstract class AbstractSubscriber<T>(
|
||||
private val bsonFactory: BsonFactory<T>,
|
||||
val size: Long = 1,
|
||||
val subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : Subscriber<Document> {
|
||||
abstract fun next(t: T)
|
||||
|
||||
private var requestRemain = 0L
|
||||
protected var requested = 0
|
||||
protected lateinit var s: Subscription
|
||||
protected var compete = false
|
||||
|
||||
override fun onComplete() {
|
||||
compete = true
|
||||
}
|
||||
|
||||
override fun onSubscribe(s: Subscription) {
|
||||
this.s = s
|
||||
requestRemain = size - requested
|
||||
subscribeExecutor.execute {
|
||||
s.request(requestRemain)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNext(t: Document) {
|
||||
next(bsonFactory.parse(t))
|
||||
requestRemain--
|
||||
requested++
|
||||
if (requestRemain > 0) return
|
||||
if (size - requested > 0) {
|
||||
s.request(size - requested)
|
||||
} else {
|
||||
onComplete()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.core.Disposable
|
||||
import cn.tursom.core.datastruct.AsyncIterator
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import org.bson.Document
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
class AsyncDocumentIteratorSubscriber(
|
||||
val bufSize: Int = 32,
|
||||
val subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : Subscriber<Document>, AsyncIterator<Document> {
|
||||
protected lateinit var s: Subscription
|
||||
|
||||
@Volatile
|
||||
protected var compete = false
|
||||
|
||||
private var cont = Disposable<Continuation<Document>>()
|
||||
private var notify = Disposable<Continuation<Unit>>()
|
||||
private val cache = ConcurrentLinkedQueue<Document>()
|
||||
|
||||
override fun onComplete() {
|
||||
cont.get()?.resumeWithException(Exception())
|
||||
notify.get()?.resume(Unit)
|
||||
compete = true
|
||||
}
|
||||
|
||||
override fun onNext(t: Document) {
|
||||
cont.get()?.resume(t) ?: cache.add(t)
|
||||
notify.get()?.resume(Unit)
|
||||
}
|
||||
|
||||
override fun onSubscribe(s: Subscription) {
|
||||
this.s = s
|
||||
subscribeExecutor.execute {
|
||||
s.request(1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
cont.get()?.resumeWithException(t) ?: t.printStackTrace()
|
||||
}
|
||||
|
||||
override suspend fun next(): Document {
|
||||
return cache.poll() ?: suspendCoroutine { cont ->
|
||||
this.cont.set(cont)
|
||||
s.request(bufSize.toLong())
|
||||
cache.poll()?.let { this.cont.get()?.resume(it) ?: cache.add(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun hasNext(): Boolean {
|
||||
if (cache.isEmpty() && !compete) {
|
||||
suspendCoroutine<Unit> {
|
||||
notify.set(it)
|
||||
s.request(bufSize.toLong())
|
||||
if (cache.isNotEmpty()) {
|
||||
notify.get()?.resume(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache.isNotEmpty()
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.core.Disposable
|
||||
import cn.tursom.core.datastruct.AsyncIterator
|
||||
import cn.tursom.database.mongodb.BsonFactory
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import org.bson.Document
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
class AsyncIteratorSubscriber<T>(
|
||||
private val bsonFactory: BsonFactory<T>,
|
||||
val bufSize: Int = 32,
|
||||
val subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : Subscriber<Document>, AsyncIterator<T>, BsonFactory<T> by bsonFactory {
|
||||
protected lateinit var s: Subscription
|
||||
|
||||
@Volatile
|
||||
protected var compete = false
|
||||
|
||||
private var cont = Disposable<Continuation<T>>()
|
||||
private var notify = Disposable<Continuation<Unit>>()
|
||||
private val cache = ConcurrentLinkedQueue<T>()
|
||||
|
||||
override fun onComplete() {
|
||||
cont.get()?.resumeWithException(Exception())
|
||||
notify.get()?.resume(Unit)
|
||||
compete = true
|
||||
}
|
||||
|
||||
override fun onNext(t: Document) {
|
||||
next(bsonFactory.parse(t))
|
||||
}
|
||||
|
||||
override fun onSubscribe(s: Subscription) {
|
||||
this.s = s
|
||||
subscribeExecutor.execute {
|
||||
s.request(1)
|
||||
}
|
||||
}
|
||||
|
||||
fun next(t: T) {
|
||||
cont.get()?.resume(t) ?: cache.add(t)
|
||||
notify.get()?.resume(Unit)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
cont.get()?.resumeWithException(t) ?: t.printStackTrace()
|
||||
}
|
||||
|
||||
override suspend fun next(): T {
|
||||
return cache.poll() ?: suspendCoroutine { cont ->
|
||||
this.cont.set(cont)
|
||||
s.request(bufSize.toLong())
|
||||
cache.poll()?.let { this.cont.get()?.resume(it) ?: cache.add(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun hasNext(): Boolean {
|
||||
if (cache.isEmpty() && !compete) {
|
||||
suspendCoroutine<Unit> {
|
||||
notify.set(it)
|
||||
s.request(bufSize.toLong())
|
||||
if (cache.isNotEmpty()) {
|
||||
notify.get()?.resume(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache.isNotEmpty()
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
class SuspendInsertListSubscriber<T>(
|
||||
val cont: Continuation<List<T>>,
|
||||
val requestSize: Long = Long.MAX_VALUE,
|
||||
val subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : Subscriber<T> {
|
||||
val resultList = ArrayList<T>()
|
||||
override fun onComplete() {
|
||||
cont.resume(resultList)
|
||||
}
|
||||
|
||||
override fun onSubscribe(s: Subscription) {
|
||||
if (requestSize > 0) subscribeExecutor.execute { s.request(requestSize) }
|
||||
else onComplete()
|
||||
}
|
||||
|
||||
override fun onNext(t: T) {
|
||||
resultList.add(t)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
cont.resumeWithException(t)
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.core.Disposable
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
class SuspendInsertOneSubscriber<T>(
|
||||
cont: Continuation<T?>,
|
||||
val requestSize: Long = 1,
|
||||
val subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : Subscriber<T> {
|
||||
val contDisposable = Disposable(cont)
|
||||
override fun onComplete() {
|
||||
contDisposable.get()?.resume(null)
|
||||
}
|
||||
|
||||
override fun onSubscribe(s: Subscription) = if (requestSize > 0) {
|
||||
subscribeExecutor.execute { s.request(requestSize) }
|
||||
} else onComplete()
|
||||
|
||||
override fun onNext(t: T) {
|
||||
contDisposable.get()?.resume(t)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
contDisposable.get()?.resumeWithException(t)
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import org.bson.Document
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
class SuspendListDocumentSubscriber(
|
||||
val cont: Continuation<List<Document>>,
|
||||
size: Long = Long.MAX_VALUE,
|
||||
subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : AbstractDocumentSubscriber(size, subscribeExecutor) {
|
||||
companion object {
|
||||
const val maxDefaultArrayCache = 4096
|
||||
}
|
||||
|
||||
val resultList = ArrayList<Document>(if (size < maxDefaultArrayCache) size.toInt() else maxDefaultArrayCache)
|
||||
override fun onComplete() = cont.resume(resultList)
|
||||
override fun onError(t: Throwable) = cont.resumeWithException(t)
|
||||
|
||||
override fun next(t: Document) {
|
||||
resultList.add(t)
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.database.mongodb.BsonFactory
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
class SuspendListSubscriber<T>(
|
||||
bsonFactory: BsonFactory<T>,
|
||||
val cont: Continuation<List<T>>,
|
||||
size: Long = Long.MAX_VALUE,
|
||||
subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : AbstractSubscriber<T>(bsonFactory, size, subscribeExecutor) {
|
||||
companion object {
|
||||
const val maxDefaultArrayCache = 4096
|
||||
}
|
||||
|
||||
val resultList = ArrayList<T>(if (size < maxDefaultArrayCache) size.toInt() else maxDefaultArrayCache)
|
||||
override fun onComplete() = cont.resume(resultList)
|
||||
override fun onError(t: Throwable) = cont.resumeWithException(t)
|
||||
|
||||
override fun next(t: T) {
|
||||
resultList.add(t)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.tursom.database.mongodb.subscriber
|
||||
|
||||
import cn.tursom.core.Disposable
|
||||
import cn.tursom.database.mongodb.BsonFactory
|
||||
import cn.tursom.database.mongodb.MongoUtil
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
@Suppress("ReactiveStreamsSubscriberImplementation")
|
||||
class SuspendOneSubscriber<T>(
|
||||
bsonFactory: BsonFactory<T>,
|
||||
cont: Continuation<T?>,
|
||||
size: Long = 1,
|
||||
subscribeExecutor: Executor = MongoUtil.mongoExecutor,
|
||||
) : AbstractSubscriber<T>(bsonFactory, size, subscribeExecutor) {
|
||||
val contDisposable = Disposable(cont)
|
||||
override fun onComplete() {
|
||||
contDisposable.get()?.resume(null)
|
||||
}
|
||||
|
||||
override fun next(t: T) {
|
||||
contDisposable.get()?.resume(t)
|
||||
}
|
||||
|
||||
override fun onError(t: Throwable) {
|
||||
contDisposable.get()?.resumeWithException(t)
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package cn.tursom.database.mongodb
|
||||
|
||||
import com.mongodb.reactivestreams.client.MongoClient
|
||||
|
||||
fun MongoClient.getMongoTemplate(db: String) = MongoTemplate(this, db)
|
33
ts-database/ts-mongodb/ts-mongodb-spring/build.gradle.kts
Normal file
33
ts-database/ts-mongodb/ts-mongodb-spring/build.gradle.kts
Normal file
@ -0,0 +1,33 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":"))
|
||||
implementation(project(":ts-core"))
|
||||
compileOnly(group = "org.springframework.data", name = "spring-data-mongodb", version = "3.1.7")
|
||||
}
|
||||
|
||||
@kotlin.Suppress("UNCHECKED_CAST")
|
||||
(rootProject.ext["excludeTest"] as (Project, TaskContainer) -> Unit)(project, tasks)
|
||||
|
||||
tasks.register("install") {
|
||||
finalizedBy(tasks["publishToMavenLocal"])
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
groupId = project.group.toString()
|
||||
artifactId = project.name
|
||||
version = project.version.toString()
|
||||
|
||||
from(components["java"])
|
||||
try {
|
||||
artifact(tasks["sourcesJar"])
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("unused")
|
||||
object AggregationBuilder : MongoName {
|
||||
inline infix operator fun invoke(
|
||||
operatorBuilder: AggregationBuilder.(MutableList<AggregationOperation>) -> MutableList<AggregationOperation>
|
||||
): Aggregation {
|
||||
val operator = this.operatorBuilder(ArrayList())
|
||||
return Aggregation.newAggregation(operator)
|
||||
}
|
||||
|
||||
infix operator fun MutableList<AggregationOperation>.plus(
|
||||
operation: AggregationOperation
|
||||
): MutableList<AggregationOperation> {
|
||||
add(operation)
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.match(
|
||||
builder: QueryBuilder.() -> Unit
|
||||
): MutableList<AggregationOperation> {
|
||||
add(Aggregation.match(QueryBuilder criteria builder))
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.group(
|
||||
builder: GroupBuilder.() -> AggregationOperation
|
||||
): MutableList<AggregationOperation> {
|
||||
add(GroupBuilder.builder())
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.project(
|
||||
builder: ProjectBuilder.() -> AggregationOperation
|
||||
): MutableList<AggregationOperation> {
|
||||
add(ProjectBuilder.builder())
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.unwind(
|
||||
field: String
|
||||
): MutableList<AggregationOperation> {
|
||||
add(Aggregation.unwind(field))
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.unwind(
|
||||
field: KProperty<*>
|
||||
): MutableList<AggregationOperation> {
|
||||
add(Aggregation.unwind(field.mongoName))
|
||||
return this
|
||||
}
|
||||
|
||||
@JvmName("unwindString")
|
||||
infix fun MutableList<AggregationOperation>.unwind(
|
||||
field: () -> String
|
||||
): MutableList<AggregationOperation> {
|
||||
add(Aggregation.unwind(field()))
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.unwind(
|
||||
field: () -> KProperty<*>
|
||||
): MutableList<AggregationOperation> {
|
||||
add(Aggregation.unwind(field().mongoName))
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.sort(
|
||||
builder: SortBuilder.() -> Unit
|
||||
): MutableList<AggregationOperation> {
|
||||
val sortBuilder = SortBuilder()
|
||||
sortBuilder.invoke(builder)
|
||||
add(sortBuilder.sortOperation)
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.skip(
|
||||
skip: Long
|
||||
): MutableList<AggregationOperation> {
|
||||
add(Aggregation.skip(skip))
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun MutableList<AggregationOperation>.limit(
|
||||
limit: Long
|
||||
): MutableList<AggregationOperation> {
|
||||
add(Aggregation.limit(limit))
|
||||
return this
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import org.bson.types.Decimal128
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* 用来将Object类型转换为Document类型的转换器
|
||||
* 设计成接口与单例对象共存的方式,即
|
||||
* 既可以用 BsonConverter.bsonValue
|
||||
* 也可以让类继承于 BsonConverter 以直接调用 Any.bsonValue 方法
|
||||
*/
|
||||
interface BsonConverter {
|
||||
companion object {
|
||||
fun bsonValue(obj: Any): Any = when (obj) {
|
||||
is BigDecimal -> Decimal128(obj)
|
||||
is String, is Number, is Boolean -> obj
|
||||
is Map<*, *> -> MongoUtil.convertToBson(obj)
|
||||
is Iterable<*> -> obj.mapNotNull { bsonValue(it ?: return@mapNotNull null) }
|
||||
is Array<*> -> obj.mapNotNull { bsonValue(it ?: return@mapNotNull null) }
|
||||
is ByteArray -> obj
|
||||
is ShortArray -> obj.asList()
|
||||
is IntArray -> obj.asList()
|
||||
is LongArray -> obj.asList()
|
||||
is FloatArray -> obj.asList()
|
||||
is DoubleArray -> obj.asList()
|
||||
is BooleanArray -> obj.asList()
|
||||
is CharArray -> obj.asList()
|
||||
else -> MongoUtil.convertToBson(obj)
|
||||
}
|
||||
}
|
||||
|
||||
fun Any.bsonValue(): Any = bsonValue(this)
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import cn.tursom.core.Parser
|
||||
import org.bson.Document
|
||||
|
||||
interface BsonFactory<T> {
|
||||
val clazz: Class<T>
|
||||
|
||||
fun parse(document: Document) = Parser.parse(document, clazz)!!
|
||||
|
||||
fun convertToBson(entity: T): Document
|
||||
|
||||
//fun convertToEntity(bson: Document): T
|
||||
|
||||
companion object {
|
||||
operator fun <T> get(clazz: Class<T>): BsonFactory<T> {
|
||||
return BsonFactoryImpl(clazz)
|
||||
}
|
||||
|
||||
inline fun <reified T : Any?> get(): BsonFactory<T> {
|
||||
return get(T::class.java)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import cn.tursom.core.isStatic
|
||||
import cn.tursom.core.isTransient
|
||||
import org.bson.Document
|
||||
|
||||
open class BsonFactoryImpl<T>(final override val clazz: Class<T>) : BsonFactory<T> {
|
||||
private val fields = clazz.declaredFields.filter {
|
||||
it.isAccessible = true
|
||||
!it.isStatic() && !it.isTransient()
|
||||
}
|
||||
|
||||
override fun convertToBson(entity: T): Document {
|
||||
val bson = Document()
|
||||
fields.forEach {
|
||||
MongoUtil.injectValue(bson, it.get(entity) ?: return@forEach, it)
|
||||
}
|
||||
return bson
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "BsonFactoryImpl(clazz=$clazz, fields=$fields)"
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import org.bson.BsonRegularExpression
|
||||
import org.bson.Document
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.domain.Example
|
||||
import org.springframework.data.geo.Circle
|
||||
import org.springframework.data.geo.Point
|
||||
import org.springframework.data.geo.Shape
|
||||
import org.springframework.data.mongodb.core.geo.GeoJson
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
import org.springframework.data.mongodb.core.schema.MongoJsonSchema
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused", "HasPlatformType")
|
||||
object CriteriaBuilder : MongoName, BsonConverter {
|
||||
const val where = "\$where"
|
||||
fun String.asJs() = where equal this
|
||||
fun String.asObjectId() = ObjectId(this)
|
||||
|
||||
infix fun where(where: String) = Criteria.where(where)
|
||||
infix fun where(where: KProperty<*>) = Criteria.where(where.mongoName)
|
||||
fun String.asWhere() = Criteria.where(this)
|
||||
fun KProperty<*>.asWhere() = mongoName.asWhere()
|
||||
|
||||
infix fun Criteria.and(where: Criteria) = Criteria().andOperator(this, where)
|
||||
infix operator fun Criteria.plus(where: Criteria) = and(where)
|
||||
|
||||
private val orOperator: Criteria.(Array<out Criteria>) -> Criteria = Criteria::orOperator
|
||||
private val andOperator: Criteria.(Array<out Criteria>) -> Criteria = Criteria::andOperator
|
||||
|
||||
fun or(vararg where: Criteria) = Criteria().orOperator(where)
|
||||
val or: (Array<out Criteria>) -> Criteria = CriteriaBuilder::or
|
||||
fun and(vararg where: Criteria) = Criteria().andOperator(where)
|
||||
val and: (Array<out Criteria>) -> Criteria = CriteriaBuilder::and
|
||||
|
||||
infix fun Criteria.or(where: Criteria) = or(this, where)
|
||||
|
||||
infix fun Criteria.gt(where: Any) = gt(where.bsonValue()) // >
|
||||
infix fun Criteria.gte(where: Any) = gte(where.bsonValue()) // >=
|
||||
|
||||
infix fun Criteria.`in`(where: Collection<Any?>) = `in`(where.mapNotNull { it?.bsonValue() }) // contains
|
||||
infix fun Criteria.`is`(where: Any?) = `is`(where?.bsonValue()) // ==
|
||||
infix fun Criteria.ne(where: Any?) = ne(where?.bsonValue()) // !=
|
||||
infix fun Criteria.lt(where: Any) = lt(where.bsonValue()) // <
|
||||
infix fun Criteria.lte(where: Any) = lte(where.bsonValue()) // <=
|
||||
infix fun Criteria.nin(where: Collection<Any?>) = nin(where.bsonValue()) // not contains
|
||||
|
||||
infix fun Criteria.all(where: Any?) = all(where?.bsonValue())
|
||||
infix fun Criteria.all(where: Collection<Any?>) = all(where.bsonValue())
|
||||
infix fun Criteria.size(s: Int) = size(s)
|
||||
infix fun Criteria.exists(b: Boolean) = exists(b)
|
||||
infix fun Criteria.type(t: Int) = type(t)
|
||||
operator fun Criteria.not() = not()
|
||||
infix fun Criteria.regex(re: String) = regex(re)
|
||||
infix fun Criteria.regex(pattern: Pattern) = regex(pattern)
|
||||
infix fun Criteria.regex(pattern: Regex) = regex(pattern.toPattern())
|
||||
infix fun Criteria.regex(regex: BsonRegularExpression) = regex(regex)
|
||||
infix fun Criteria.withinSphere(circle: Circle) = withinSphere(circle)
|
||||
infix fun Criteria.within(shape: Shape) = within(shape)
|
||||
infix fun Criteria.near(point: Point) = near(point)
|
||||
infix fun Criteria.nearSphere(point: Point) = nearSphere(point)
|
||||
infix fun Criteria.intersects(point: GeoJson<*>) = intersects(point)
|
||||
infix fun Criteria.maxDistance(maxDistance: Double) = maxDistance(maxDistance)
|
||||
infix fun Criteria.minDistance(maxDistance: Double) = minDistance(maxDistance)
|
||||
infix fun Criteria.elemMatch(c: Criteria) = elemMatch(c)
|
||||
infix fun Criteria.alike(sample: Example<*>) = alike(sample)
|
||||
infix fun Criteria.andDocumentStructureMatches(schema: MongoJsonSchema) = andDocumentStructureMatches(schema)
|
||||
|
||||
infix fun Criteria.eq(where: Any?) = `is`(where?.bsonValue())
|
||||
infix fun Criteria.equal(where: Any?) = `is`(where?.bsonValue())
|
||||
|
||||
infix fun KProperty<*>.gt(where: Any) = asWhere() gt where // >
|
||||
infix fun KProperty<*>.gte(where: Any) = asWhere() gte where // >=
|
||||
fun KProperty<*>.`in`(vararg where: Any?) = asWhere() `in` where.asList() // contains
|
||||
infix fun KProperty<*>.`in`(where: Collection<Any?>) = asWhere() `in` where // contains
|
||||
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 // <
|
||||
infix fun KProperty<*>.lte(where: Any) = asWhere() lte where // <=
|
||||
fun KProperty<*>.nin(vararg where: Any?) = asWhere() nin where.asList() // not contains
|
||||
infix fun KProperty<*>.nin(where: Collection<Any?>) = asWhere() nin where // not contains
|
||||
|
||||
infix fun KProperty<*>.all(where: Any?) = asWhere() all where
|
||||
infix fun KProperty<*>.all(where: Collection<Any?>) = asWhere() all where
|
||||
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()
|
||||
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.toPattern()
|
||||
infix fun KProperty<*>.regex(regex: BsonRegularExpression) = asWhere() regex regex
|
||||
infix fun KProperty<*>.withinSphere(circle: Circle) = asWhere() withinSphere circle
|
||||
infix fun KProperty<*>.within(shape: Shape) = asWhere() within shape
|
||||
infix fun KProperty<*>.near(point: Point) = asWhere() near point
|
||||
infix fun KProperty<*>.nearSphere(point: Point) = asWhere() nearSphere point
|
||||
infix fun KProperty<*>.intersects(point: GeoJson<*>) = asWhere() intersects point
|
||||
infix fun KProperty<*>.maxDistance(maxDistance: Double) = asWhere() maxDistance maxDistance
|
||||
infix fun KProperty<*>.minDistance(maxDistance: Double) = asWhere() minDistance maxDistance
|
||||
infix fun KProperty<*>.elemMatch(c: Criteria) = asWhere() elemMatch c
|
||||
infix fun KProperty<*>.alike(sample: Example<*>) = asWhere() alike sample
|
||||
infix fun KProperty<*>.andDocumentStructureMatches(schema: MongoJsonSchema) =
|
||||
asWhere() andDocumentStructureMatches schema
|
||||
|
||||
infix fun KProperty<*>.eq(where: Any?) = asWhere() equal where
|
||||
infix fun KProperty<*>.equal(where: Any?) = asWhere() equal where
|
||||
|
||||
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.asList() // contains
|
||||
infix fun String.`in`(where: Collection<Any?>) = asWhere() `in` where // contains
|
||||
infix fun String.`is`(where: Any?) = asWhere() `is` where // ==
|
||||
infix fun String.ne(where: Any?) = asWhere() ne where // !=
|
||||
infix fun String.lt(where: Any) = asWhere() lt where // <
|
||||
infix fun String.lte(where: Any) = asWhere() lte where // <=
|
||||
fun String.nin(vararg where: Any?) = asWhere() nin where.asList() // not contains
|
||||
infix fun String.nin(where: Collection<Any?>) = asWhere() nin where // not contains
|
||||
|
||||
infix fun String.all(where: Any?) = asWhere() all where
|
||||
infix fun String.all(where: Collection<Any?>) = asWhere() all where
|
||||
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()
|
||||
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()
|
||||
infix fun String.regex(regex: BsonRegularExpression) = asWhere() regex regex
|
||||
infix fun String.withinSphere(circle: Circle) = asWhere() withinSphere circle
|
||||
infix fun String.within(shape: Shape) = asWhere() within shape
|
||||
infix fun String.near(point: Point) = asWhere() near point
|
||||
infix fun String.nearSphere(point: Point) = asWhere() nearSphere point
|
||||
infix fun String.intersects(point: GeoJson<*>) = asWhere() intersects point
|
||||
infix fun String.maxDistance(maxDistance: Double) = asWhere() maxDistance maxDistance
|
||||
infix fun String.minDistance(maxDistance: Double) = asWhere() minDistance maxDistance
|
||||
infix fun String.elemMatch(c: Criteria) = asWhere() elemMatch c
|
||||
infix fun String.alike(sample: Example<*>) = asWhere() alike sample
|
||||
infix fun String.andDocumentStructureMatches(schema: MongoJsonSchema) = asWhere() andDocumentStructureMatches schema
|
||||
|
||||
infix fun String.eq(where: Any?) = asWhere() equal where
|
||||
infix fun String.equal(where: Any?) = asWhere() equal where
|
||||
|
||||
inline infix operator fun invoke(operator: CriteriaBuilder.() -> Criteria): Criteria = this.operator()
|
||||
inline infix fun query(operator: CriteriaBuilder.() -> Criteria): Query = Query(this.operator())
|
||||
inline infix fun queryObject(operator: CriteriaBuilder.() -> Criteria): Document =
|
||||
Query(this.operator()).queryObject
|
||||
|
||||
inline infix fun fieldsObject(operator: CriteriaBuilder.() -> Criteria): Document =
|
||||
Query(this.operator()).fieldsObject
|
||||
|
||||
inline infix fun sortObject(operator: CriteriaBuilder.() -> Criteria): Document = Query(this.operator()).sortObject
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
|
||||
object CriteriaQuery : MongoName, BsonConverter {
|
||||
inline infix operator fun invoke(operator: CriteriaBuilder.() -> Criteria): Query = Query(CriteriaBuilder.operator())
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation
|
||||
import org.springframework.data.mongodb.core.aggregation.GroupOperation
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("unused")
|
||||
object GroupBuilder : MongoName, BsonConverter {
|
||||
inline infix operator fun invoke(operator: GroupBuilder.() -> GroupOperation): GroupOperation = this.operator()
|
||||
|
||||
fun group(vararg fields: String): GroupOperation = Aggregation.group(*fields)
|
||||
fun group(vararg fields: KProperty<*>): GroupOperation =
|
||||
Aggregation.group(*fields.map { it.mongoName }.toTypedArray())
|
||||
|
||||
infix fun GroupOperation.sum(reference: String): GroupOperation.GroupOperationBuilder = sum(reference)
|
||||
infix fun GroupOperation.sum(reference: KProperty<*>): GroupOperation.GroupOperationBuilder = sum(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.GroupOperationBuilder.`as`(alias: String): GroupOperation = `as`(alias)
|
||||
infix fun GroupOperation.GroupOperationBuilder.`as`(alias: KProperty<*>): GroupOperation = `as`(alias.mongoName)
|
||||
|
||||
infix fun GroupOperation.addToSet(reference: String): GroupOperation.GroupOperationBuilder = addToSet(reference)
|
||||
infix fun GroupOperation.addToSet(reference: KProperty<*>): GroupOperation.GroupOperationBuilder =
|
||||
addToSet(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.addToSet(value: Any): GroupOperation.GroupOperationBuilder = addToSet(value)
|
||||
|
||||
infix fun GroupOperation.last(reference: String): GroupOperation.GroupOperationBuilder = last(reference)
|
||||
infix fun GroupOperation.last(reference: KProperty<*>): GroupOperation.GroupOperationBuilder =
|
||||
last(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.first(reference: String): GroupOperation.GroupOperationBuilder = first(reference)
|
||||
infix fun GroupOperation.first(reference: KProperty<*>): GroupOperation.GroupOperationBuilder =
|
||||
first(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.avg(reference: String): GroupOperation.GroupOperationBuilder = avg(reference)
|
||||
infix fun GroupOperation.avg(reference: KProperty<*>): GroupOperation.GroupOperationBuilder = avg(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.push(reference: String): GroupOperation.GroupOperationBuilder = push(reference)
|
||||
infix fun GroupOperation.push(reference: KProperty<*>): GroupOperation.GroupOperationBuilder =
|
||||
push(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.push(value: Any): GroupOperation.GroupOperationBuilder = push(value)
|
||||
|
||||
infix fun GroupOperation.min(reference: String): GroupOperation.GroupOperationBuilder = min(reference)
|
||||
infix fun GroupOperation.min(reference: KProperty<*>): GroupOperation.GroupOperationBuilder = min(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.max(reference: String): GroupOperation.GroupOperationBuilder = max(reference)
|
||||
infix fun GroupOperation.max(reference: KProperty<*>): GroupOperation.GroupOperationBuilder = max(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.stdDevSamp(reference: String): GroupOperation.GroupOperationBuilder = stdDevSamp(reference)
|
||||
infix fun GroupOperation.stdDevSamp(reference: KProperty<*>): GroupOperation.GroupOperationBuilder =
|
||||
stdDevSamp(reference.mongoName)
|
||||
|
||||
infix fun GroupOperation.stdDevPop(reference: String): GroupOperation.GroupOperationBuilder = stdDevPop(reference)
|
||||
infix fun GroupOperation.stdDevPop(reference: KProperty<*>): GroupOperation.GroupOperationBuilder =
|
||||
stdDevPop(reference.mongoName)
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import cn.tursom.database.mongodb.spring.function.Function1
|
||||
import cn.tursom.database.mongodb.spring.function.field
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.mongodb.core.mapping.Field
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
|
||||
interface MongoName {
|
||||
companion object {
|
||||
const val dollar = '$'
|
||||
inline operator fun <T> invoke(builder: Companion.() -> T) = this.builder()
|
||||
|
||||
inline val KProperty<*>.mongoName: String
|
||||
get() = mongoName(this)
|
||||
|
||||
fun arrayElement(elementName: String = "") = if (elementName.isBlank()) {
|
||||
"$"
|
||||
} else {
|
||||
"$[$elementName]"
|
||||
}
|
||||
|
||||
fun mongoName(kProperty: KProperty<*>): String {
|
||||
if (kProperty.javaField?.getAnnotation(Id::class.java) != null) {
|
||||
return "_id"
|
||||
}
|
||||
val field = kProperty.javaField?.getAnnotation(Field::class.java)
|
||||
if (field != null) {
|
||||
if (field.value.isNotEmpty()) {
|
||||
return field.value
|
||||
}
|
||||
}
|
||||
return kProperty.name
|
||||
}
|
||||
|
||||
fun mongoName(field: java.lang.reflect.Field): String {
|
||||
if (field.getAnnotation(Id::class.java) != null) {
|
||||
return "_id"
|
||||
}
|
||||
val fieldAnnotation = field.getAnnotation(Field::class.java)
|
||||
if (fieldAnnotation != null) {
|
||||
if (fieldAnnotation.value.isNotEmpty()) {
|
||||
return fieldAnnotation.value
|
||||
}
|
||||
}
|
||||
return field.name
|
||||
}
|
||||
|
||||
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<*>.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<*>.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)
|
||||
}
|
||||
|
||||
val dollar get() = Companion.dollar
|
||||
val KProperty<*>.mongoName: String
|
||||
get() = mongoName(this)
|
||||
|
||||
fun arrayElement(elementName: String = "") = Companion.arrayElement(elementName)
|
||||
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 }
|
||||
|
||||
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 }
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import cn.tursom.core.isStatic
|
||||
import cn.tursom.core.isTransient
|
||||
import cn.tursom.core.uncheckedCast
|
||||
import org.bson.BsonValue
|
||||
import org.bson.Document
|
||||
import org.bson.conversions.Bson
|
||||
import java.lang.reflect.Field
|
||||
|
||||
object MongoUtil {
|
||||
fun collectionName(clazz: Class<*>) =
|
||||
clazz.getAnnotation(org.springframework.data.mongodb.core.mapping.Document::class.java)?.let {
|
||||
when {
|
||||
it.value.isNotBlank() -> it.value
|
||||
it.collection.isNotBlank() -> it.collection
|
||||
else -> null
|
||||
}
|
||||
} ?: clazz.simpleName.toCharArray().let {
|
||||
it[0] = it[0].toLowerCase()
|
||||
it
|
||||
}.concatToString()
|
||||
|
||||
fun convertToBson(entity: Any): Document {
|
||||
return when (entity) {
|
||||
is Document -> entity
|
||||
is Map<*, *> -> entity.convert()
|
||||
else -> {
|
||||
val bson = Document()
|
||||
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())
|
||||
}.forEach {
|
||||
injectValue(bson, it.get(entity) ?: return@forEach, it)
|
||||
}
|
||||
bson
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun injectValue(bson: Document, value: Any, field: Field) {
|
||||
when (value) {
|
||||
is Pair<*, *> -> bson[value.first?.toString() ?: return] = convertToBson(value.second ?: return)
|
||||
is Map.Entry<*, *> -> bson[value.key?.toString() ?: return] =
|
||||
convertToBson(value.value ?: return)
|
||||
else -> bson[MongoName.mongoName(field)] = value.convert() ?: return
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> fromBson(document: Document, bsonFactory: BsonFactory<T>) = bsonFactory.parse(document)
|
||||
inline fun <reified T> fromBson(document: Document) = BsonFactory[T::class.java].parse(document)
|
||||
|
||||
private fun Iterator<*>.convert(): List<*> {
|
||||
val list = ArrayList<Any?>()
|
||||
forEach {
|
||||
list.add(it.convert() ?: return@forEach)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
private fun Map<*, *>.convert(): Document {
|
||||
val doc = Document()
|
||||
forEach { any, u ->
|
||||
any ?: return@forEach
|
||||
doc[any.toString()] = u.convert() ?: return@forEach
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
private fun Any?.convert() = when (this) {
|
||||
null -> null
|
||||
is Enum<*> -> name
|
||||
is Boolean, is Number, is String, is Bson, is BsonValue -> this
|
||||
is Map<*, *> -> this.convert()
|
||||
is Iterator<*> -> this.convert()
|
||||
is Iterable<*> -> this.iterator().convert()
|
||||
else -> convertToBson(this)
|
||||
}
|
||||
}
|
@ -0,0 +1,324 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation
|
||||
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
object ProjectBuilder : MongoName, BsonConverter {
|
||||
inline infix operator fun invoke(
|
||||
operator: ProjectBuilder.() -> ProjectionOperation
|
||||
): ProjectionOperation = this.operator()
|
||||
|
||||
fun project(): ProjectionOperation = Aggregation.project()
|
||||
fun project(vararg fields: String): ProjectionOperation = Aggregation.project(*fields)
|
||||
fun project(vararg fields: KProperty<*>): ProjectionOperation =
|
||||
Aggregation.project(*fields.map { it.mongoName }.toTypedArray())
|
||||
|
||||
infix fun ProjectionOperation.and(name: String): ProjectionOperationBuilder = and(name)
|
||||
infix fun ProjectionOperation.and(name: KProperty<*>): ProjectionOperationBuilder = and(name.mongoName)
|
||||
|
||||
infix fun ProjectionOperation.andExclude(fields: KProperty<*>): ProjectionOperation = andExclude(listOf(fields))
|
||||
fun ProjectionOperation.andExclude(vararg fields: KProperty<*>): ProjectionOperation = andExclude(fields.asList())
|
||||
infix fun ProjectionOperation.andExclude(fields: Collection<KProperty<*>>): ProjectionOperation =
|
||||
andExclude(*fields.map { it.mongoName }.toTypedArray())
|
||||
|
||||
infix fun ProjectionOperation.andInclude(fields: KProperty<*>): ProjectionOperation = andInclude(listOf(fields))
|
||||
fun ProjectionOperation.andInclude(vararg fields: KProperty<*>): ProjectionOperation = andInclude(fields.asList())
|
||||
infix fun ProjectionOperation.andInclude(fields: Collection<KProperty<*>>): ProjectionOperation =
|
||||
andInclude(*fields.map { it.mongoName }.toTypedArray())
|
||||
|
||||
|
||||
infix fun ProjectionOperationBuilder.nested(fields: Fields): ProjectionOperation = nested(fields)
|
||||
infix fun ProjectionOperationBuilder.`as`(alias: String): ProjectionOperation = `as`(alias)
|
||||
infix fun ProjectionOperationBuilder.alias(alias: String): ProjectionOperation = `as`(alias)
|
||||
infix fun ProjectionOperationBuilder.`as`(alias: KProperty<*>): ProjectionOperation = `as`(alias.mongoName)
|
||||
infix fun ProjectionOperationBuilder.alias(alias: KProperty<*>): ProjectionOperation = `as`(alias.mongoName)
|
||||
infix fun ProjectionOperationBuilder.applyCondition(cond: ConditionalOperators.Cond): ProjectionOperation =
|
||||
applyCondition(cond)
|
||||
|
||||
infix fun ProjectionOperationBuilder.applyCondition(ifNull: ConditionalOperators.IfNull): ProjectionOperation =
|
||||
applyCondition(ifNull)
|
||||
|
||||
infix operator fun ProjectionOperationBuilder.plus(number: Number): ProjectionOperationBuilder = plus(number)
|
||||
infix operator fun ProjectionOperationBuilder.plus(fieldReference: String): ProjectionOperationBuilder =
|
||||
plus(fieldReference)
|
||||
|
||||
infix operator fun ProjectionOperationBuilder.plus(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
plus(fieldReference.mongoName)
|
||||
|
||||
infix operator fun ProjectionOperationBuilder.minus(number: Number): ProjectionOperationBuilder = minus(number)
|
||||
infix operator fun ProjectionOperationBuilder.minus(fieldReference: String): ProjectionOperationBuilder =
|
||||
minus(fieldReference)
|
||||
|
||||
infix operator fun ProjectionOperationBuilder.minus(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
minus(fieldReference.mongoName)
|
||||
|
||||
infix fun ProjectionOperationBuilder.multiply(number: Number): ProjectionOperationBuilder = multiply(number)
|
||||
infix fun ProjectionOperationBuilder.multiply(fieldReference: String): ProjectionOperationBuilder =
|
||||
multiply(fieldReference)
|
||||
|
||||
infix fun ProjectionOperationBuilder.multiply(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
multiply(fieldReference.mongoName)
|
||||
|
||||
infix operator fun ProjectionOperationBuilder.times(number: Number): ProjectionOperationBuilder = multiply(number)
|
||||
infix operator fun ProjectionOperationBuilder.times(fieldReference: String): ProjectionOperationBuilder =
|
||||
multiply(fieldReference)
|
||||
|
||||
infix operator fun ProjectionOperationBuilder.times(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
multiply(fieldReference.mongoName)
|
||||
|
||||
infix fun ProjectionOperationBuilder.divide(number: Number): ProjectionOperationBuilder = divide(number)
|
||||
infix fun ProjectionOperationBuilder.divide(fieldReference: String): ProjectionOperationBuilder =
|
||||
divide(fieldReference)
|
||||
|
||||
infix fun ProjectionOperationBuilder.divide(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
divide(fieldReference.mongoName)
|
||||
|
||||
infix operator fun ProjectionOperationBuilder.div(number: Number): ProjectionOperationBuilder = divide(number)
|
||||
infix operator fun ProjectionOperationBuilder.div(fieldReference: String): ProjectionOperationBuilder =
|
||||
divide(fieldReference)
|
||||
|
||||
infix operator fun ProjectionOperationBuilder.div(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
divide(fieldReference.mongoName)
|
||||
|
||||
infix fun ProjectionOperationBuilder.mod(number: Number): ProjectionOperationBuilder = mod(number)
|
||||
infix fun ProjectionOperationBuilder.mod(fieldReference: String): ProjectionOperationBuilder = mod(fieldReference)
|
||||
infix fun ProjectionOperationBuilder.mod(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
mod(fieldReference.mongoName)
|
||||
|
||||
infix fun ProjectionOperationBuilder.cmp(compareValue: Any): ProjectionOperationBuilder = cmp(compareValue)
|
||||
infix fun ProjectionOperationBuilder.eq(compareValue: Any): ProjectionOperationBuilder = eq(compareValue)
|
||||
infix fun ProjectionOperationBuilder.gt(compareValue: Any): ProjectionOperationBuilder = gt(compareValue)
|
||||
infix fun ProjectionOperationBuilder.gte(compareValue: Any): ProjectionOperationBuilder = gte(compareValue)
|
||||
infix fun ProjectionOperationBuilder.lt(compareValue: Any): ProjectionOperationBuilder = lt(compareValue)
|
||||
infix fun ProjectionOperationBuilder.lte(compareValue: Any): ProjectionOperationBuilder = lte(compareValue)
|
||||
infix fun ProjectionOperationBuilder.ne(compareValue: Any): ProjectionOperationBuilder = ne(compareValue)
|
||||
|
||||
infix fun ProjectionOperationBuilder.slice(count: Int): ProjectionOperationBuilder = slice(count)
|
||||
|
||||
infix fun ProjectionOperationBuilder.log(baseFieldRef: String): ProjectionOperationBuilder = log(baseFieldRef)
|
||||
infix fun ProjectionOperationBuilder.log(baseFieldRef: KProperty<*>): ProjectionOperationBuilder =
|
||||
log(baseFieldRef.mongoName)
|
||||
|
||||
infix fun ProjectionOperationBuilder.log(base: Number): ProjectionOperationBuilder = log(base)
|
||||
|
||||
infix fun ProjectionOperationBuilder.pow(exponentFieldRef: String): ProjectionOperationBuilder = pow(exponentFieldRef)
|
||||
infix fun ProjectionOperationBuilder.pow(exponentFieldRef: KProperty<*>): ProjectionOperationBuilder =
|
||||
pow(exponentFieldRef.mongoName)
|
||||
|
||||
infix fun ProjectionOperationBuilder.pow(exponent: Number): ProjectionOperationBuilder = pow(exponent)
|
||||
|
||||
infix fun ProjectionOperationBuilder.strCaseCmpValueOf(fieldRef: String): ProjectionOperationBuilder =
|
||||
strCaseCmpValueOf(fieldRef)
|
||||
|
||||
infix fun ProjectionOperationBuilder.strCaseCmpValueOf(fieldRef: KProperty<*>): ProjectionOperationBuilder =
|
||||
strCaseCmpValueOf(fieldRef.mongoName)
|
||||
|
||||
@JvmName("concatArrays_")
|
||||
fun ProjectionOperationBuilder.concatArrays(fields: Collection<String>): ProjectionOperationBuilder =
|
||||
concatArrays(*fields.toTypedArray())
|
||||
|
||||
fun ProjectionOperationBuilder.concatArrays(vararg fields: KProperty<*>): ProjectionOperationBuilder =
|
||||
concatArrays(*fields.map { it.mongoName }.toTypedArray())
|
||||
|
||||
fun ProjectionOperationBuilder.concatArrays(fields: Collection<KProperty<*>>): ProjectionOperationBuilder =
|
||||
concatArrays(*fields.map { it.mongoName }.toTypedArray())
|
||||
|
||||
|
||||
infix fun String.nested(fields: Fields): ProjectionOperation = project() and this nested (fields)
|
||||
infix fun String.`as`(alias: String): ProjectionOperation = project() and this `as` (alias)
|
||||
infix fun String.alias(alias: String): ProjectionOperation = project() and this `as` (alias)
|
||||
infix fun String.`as`(alias: KProperty<*>): ProjectionOperation = project() and this `as` (alias.mongoName)
|
||||
infix fun String.alias(alias: KProperty<*>): ProjectionOperation = project() and this `as` (alias.mongoName)
|
||||
infix fun String.applyCondition(cond: ConditionalOperators.Cond): ProjectionOperation =
|
||||
project() and this applyCondition (cond)
|
||||
|
||||
infix fun String.applyCondition(ifNull: ConditionalOperators.IfNull): ProjectionOperation =
|
||||
project() and this applyCondition (ifNull)
|
||||
|
||||
infix operator fun String.plus(number: Number): ProjectionOperationBuilder = project() and this plus (number)
|
||||
infix operator fun String.plus(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this plus (fieldReference)
|
||||
|
||||
infix operator fun String.plus(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this plus (fieldReference.mongoName)
|
||||
|
||||
infix operator fun String.minus(number: Number): ProjectionOperationBuilder = project() and this minus (number)
|
||||
infix operator fun String.minus(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this minus (fieldReference)
|
||||
|
||||
infix operator fun String.minus(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this minus (fieldReference.mongoName)
|
||||
|
||||
infix fun String.multiply(number: Number): ProjectionOperationBuilder = project() and this multiply (number)
|
||||
infix fun String.multiply(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this multiply (fieldReference)
|
||||
|
||||
infix fun String.multiply(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this multiply (fieldReference.mongoName)
|
||||
|
||||
infix operator fun String.times(number: Number): ProjectionOperationBuilder = project() and this multiply (number)
|
||||
infix operator fun String.times(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this multiply (fieldReference)
|
||||
|
||||
infix operator fun String.times(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this multiply (fieldReference.mongoName)
|
||||
|
||||
infix fun String.divide(number: Number): ProjectionOperationBuilder = project() and this divide (number)
|
||||
infix fun String.divide(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this divide (fieldReference)
|
||||
|
||||
infix fun String.divide(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this divide (fieldReference.mongoName)
|
||||
|
||||
infix operator fun String.div(number: Number): ProjectionOperationBuilder = project() and this divide (number)
|
||||
infix operator fun String.div(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this divide (fieldReference)
|
||||
|
||||
infix operator fun String.div(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this divide (fieldReference.mongoName)
|
||||
|
||||
infix fun String.mod(number: Number): ProjectionOperationBuilder = project() and this mod (number)
|
||||
infix fun String.mod(fieldReference: String): ProjectionOperationBuilder = project() and this mod (fieldReference)
|
||||
infix fun String.mod(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this mod (fieldReference.mongoName)
|
||||
|
||||
infix fun String.cmp(compareValue: Any): ProjectionOperationBuilder = project() and this cmp (compareValue)
|
||||
infix fun String.eq(compareValue: Any): ProjectionOperationBuilder = project() and this eq (compareValue)
|
||||
infix fun String.gt(compareValue: Any): ProjectionOperationBuilder = project() and this gt (compareValue)
|
||||
infix fun String.gte(compareValue: Any): ProjectionOperationBuilder = project() and this gte (compareValue)
|
||||
infix fun String.lt(compareValue: Any): ProjectionOperationBuilder = project() and this lt (compareValue)
|
||||
infix fun String.lte(compareValue: Any): ProjectionOperationBuilder = project() and this lte (compareValue)
|
||||
infix fun String.ne(compareValue: Any): ProjectionOperationBuilder = project() and this ne (compareValue)
|
||||
|
||||
infix fun String.slice(count: Int): ProjectionOperationBuilder = project() and this slice (count)
|
||||
fun String.slice(count: Int, offset: Int): ProjectionOperationBuilder = (project() and this).slice(count, offset)
|
||||
|
||||
infix fun String.log(baseFieldRef: String): ProjectionOperationBuilder = project() and this log (baseFieldRef)
|
||||
infix fun String.log(baseFieldRef: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this log (baseFieldRef.mongoName)
|
||||
|
||||
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: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this pow (exponentFieldRef.mongoName)
|
||||
|
||||
infix fun String.pow(exponent: Number): ProjectionOperationBuilder = project() and this pow (exponent)
|
||||
|
||||
infix fun String.strCaseCmpValueOf(fieldRef: String): ProjectionOperationBuilder =
|
||||
project() and this strCaseCmpValueOf (fieldRef)
|
||||
|
||||
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)
|
||||
infix fun KProperty<*>.`as`(alias: KProperty<*>): ProjectionOperation = project() and this `as` (alias.mongoName)
|
||||
infix fun KProperty<*>.alias(alias: KProperty<*>): ProjectionOperation = project() and this `as` (alias.mongoName)
|
||||
infix fun KProperty<*>.applyCondition(cond: ConditionalOperators.Cond): ProjectionOperation =
|
||||
project() and this applyCondition (cond)
|
||||
|
||||
infix fun KProperty<*>.applyCondition(ifNull: ConditionalOperators.IfNull): ProjectionOperation =
|
||||
project() and this applyCondition (ifNull)
|
||||
|
||||
infix operator fun KProperty<*>.plus(number: Number): ProjectionOperationBuilder = project() and this plus (number)
|
||||
infix operator fun KProperty<*>.plus(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this plus (fieldReference)
|
||||
|
||||
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(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this minus (fieldReference)
|
||||
|
||||
infix operator fun KProperty<*>.minus(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this minus (fieldReference.mongoName)
|
||||
|
||||
infix fun KProperty<*>.multiply(number: Number): ProjectionOperationBuilder = project() and this multiply (number)
|
||||
infix fun KProperty<*>.multiply(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this multiply (fieldReference)
|
||||
|
||||
infix fun KProperty<*>.multiply(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this multiply (fieldReference.mongoName)
|
||||
|
||||
infix operator fun KProperty<*>.times(number: Number): ProjectionOperationBuilder =
|
||||
project() and this multiply (number)
|
||||
|
||||
infix operator fun KProperty<*>.times(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this multiply (fieldReference)
|
||||
|
||||
infix operator fun KProperty<*>.times(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this multiply (fieldReference.mongoName)
|
||||
|
||||
infix fun KProperty<*>.divide(number: Number): ProjectionOperationBuilder = project() and this divide (number)
|
||||
infix fun KProperty<*>.divide(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this divide (fieldReference)
|
||||
|
||||
infix fun KProperty<*>.divide(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this divide (fieldReference.mongoName)
|
||||
|
||||
infix operator fun KProperty<*>.div(number: Number): ProjectionOperationBuilder = project() and this divide (number)
|
||||
infix operator fun KProperty<*>.div(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this divide (fieldReference)
|
||||
|
||||
infix operator fun KProperty<*>.div(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this divide (fieldReference.mongoName)
|
||||
|
||||
infix fun KProperty<*>.mod(number: Number): ProjectionOperationBuilder = project() and this mod (number)
|
||||
infix fun KProperty<*>.mod(fieldReference: String): ProjectionOperationBuilder =
|
||||
project() and this mod (fieldReference)
|
||||
|
||||
infix fun KProperty<*>.mod(fieldReference: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this mod (fieldReference.mongoName)
|
||||
|
||||
infix fun KProperty<*>.cmp(compareValue: Any): ProjectionOperationBuilder = project() and this cmp (compareValue)
|
||||
infix fun KProperty<*>.eq(compareValue: Any): ProjectionOperationBuilder = project() and this eq (compareValue)
|
||||
infix fun KProperty<*>.gt(compareValue: Any): ProjectionOperationBuilder = project() and this gt (compareValue)
|
||||
infix fun KProperty<*>.gte(compareValue: Any): ProjectionOperationBuilder = project() and this gte (compareValue)
|
||||
infix fun KProperty<*>.lt(compareValue: Any): ProjectionOperationBuilder = project() and this lt (compareValue)
|
||||
infix fun KProperty<*>.lte(compareValue: Any): ProjectionOperationBuilder = project() and this lte (compareValue)
|
||||
infix fun KProperty<*>.ne(compareValue: Any): ProjectionOperationBuilder = project() and this ne (compareValue)
|
||||
|
||||
infix fun KProperty<*>.slice(count: Int): ProjectionOperationBuilder = project() and this slice (count)
|
||||
fun KProperty<*>.slice(count: Int, offset: Int): ProjectionOperationBuilder =
|
||||
(project() and this).slice(count, offset)
|
||||
|
||||
infix fun KProperty<*>.log(baseFieldRef: String): ProjectionOperationBuilder = project() and this log (baseFieldRef)
|
||||
infix fun KProperty<*>.log(baseFieldRef: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this log (baseFieldRef.mongoName)
|
||||
|
||||
infix fun KProperty<*>.log(base: Number): ProjectionOperationBuilder = project() and this log (base)
|
||||
|
||||
infix fun KProperty<*>.pow(exponentFieldRef: String): ProjectionOperationBuilder =
|
||||
project() and this pow (exponentFieldRef)
|
||||
|
||||
infix fun KProperty<*>.pow(exponentFieldRef: KProperty<*>): ProjectionOperationBuilder =
|
||||
project() and this pow (exponentFieldRef.mongoName)
|
||||
|
||||
infix fun KProperty<*>.pow(exponent: Number): ProjectionOperationBuilder = project() and this pow (exponent)
|
||||
|
||||
infix fun KProperty<*>.strCaseCmpValueOf(fieldRef: String): ProjectionOperationBuilder =
|
||||
project() and this strCaseCmpValueOf (fieldRef)
|
||||
|
||||
infix fun KProperty<*>.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())
|
||||
|
||||
fun String.concatArrays(vararg fields: KProperty<*>): ProjectionOperationBuilder =
|
||||
(project() and this).concatArrays(*fields.map { it.mongoName }.toTypedArray())
|
||||
|
||||
fun String.concatArrays(fields: Collection<KProperty<*>>): ProjectionOperationBuilder =
|
||||
(project() and this).concatArrays(*fields.map { it.mongoName }.toTypedArray())
|
||||
|
||||
}
|
@ -0,0 +1,331 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import cn.tursom.core.uncheckedCast
|
||||
import org.bson.BsonRegularExpression
|
||||
import org.bson.Document
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.domain.Example
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.geo.Circle
|
||||
import org.springframework.data.geo.Point
|
||||
import org.springframework.data.geo.Shape
|
||||
import org.springframework.data.mongodb.core.geo.GeoJson
|
||||
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.schema.MongoJsonSchema
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||
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().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
|
||||
}
|
||||
|
||||
val queryHandler = LinkedList<Query.() -> Unit>()
|
||||
private var uniqueField = true
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
val or: Criteria
|
||||
get() = when (criteriaList.size) {
|
||||
0 -> Criteria()
|
||||
1 -> criteriaList.first()
|
||||
else -> Criteria().orOperator(*criteriaList.toTypedArray())
|
||||
}
|
||||
|
||||
private val criteriaList = ArrayList<Criteria>()
|
||||
|
||||
fun query(handler: Query.() -> Unit) {
|
||||
queryHandler.add(handler)
|
||||
}
|
||||
|
||||
fun String.asJs() = where equal this
|
||||
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) }
|
||||
|
||||
//infix fun Criteria.or(operator: QueryBuilder.() -> Unit) {
|
||||
// criteriaList.add(QueryBuilder().also(operator).criteria)
|
||||
//}
|
||||
|
||||
fun or(operator: QueryBuilder.() -> Unit) {
|
||||
QueryBuilder().also(operator).or.also(criteriaList::add)
|
||||
}
|
||||
|
||||
fun and(operator: QueryBuilder.() -> Unit) {
|
||||
QueryBuilder().also(operator).and.also(criteriaList::add)
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.gt(where: Any) = asWhere() gt where // >
|
||||
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 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 // <
|
||||
infix fun KProperty<*>.lte(where: Any) = asWhere() lte where // <=
|
||||
fun KProperty<*>.nin(vararg where: Any?) = asWhere() nin where // not contains
|
||||
infix fun KProperty<*>.nin(where: Collection<Any?>) = asWhere() nin where // not contains
|
||||
|
||||
infix fun KProperty<*>.all(where: Any?) = asWhere() all where
|
||||
infix fun KProperty<*>.all(where: Collection<Any?>) = asWhere() all where
|
||||
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)
|
||||
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
|
||||
infix fun KProperty<*>.regex(regex: BsonRegularExpression) = asWhere() regex regex
|
||||
infix fun KProperty<*>.withinSphere(circle: Circle) = asWhere() withinSphere circle
|
||||
infix fun KProperty<*>.within(shape: Shape) = asWhere() within shape
|
||||
infix fun KProperty<*>.near(point: Point) = asWhere() near point
|
||||
infix fun KProperty<*>.nearSphere(point: Point) = asWhere() nearSphere point
|
||||
infix fun KProperty<*>.intersects(point: GeoJson<*>) = asWhere() intersects point
|
||||
infix fun KProperty<*>.maxDistance(maxDistance: Double) = asWhere() maxDistance maxDistance
|
||||
infix fun KProperty<*>.minDistance(maxDistance: Double) = asWhere() minDistance maxDistance
|
||||
infix fun KProperty<*>.elemMatch(c: QueryBuilder.() -> Unit) = asWhere() elemMatch c
|
||||
infix fun KProperty<*>.alike(sample: Example<*>) = asWhere() alike sample
|
||||
infix fun KProperty<*>.andDocumentStructureMatches(schema: MongoJsonSchema) =
|
||||
asWhere() andDocumentStructureMatches schema
|
||||
|
||||
infix fun KProperty<*>.equal(where: Any?) = asWhere() equal where
|
||||
infix fun KProperty<*>.eq(where: Any?) = asWhere() equal where
|
||||
|
||||
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 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
|
||||
infix fun String.`in`(where: Collection<Any?>) = asWhere() `in` where // contains
|
||||
infix fun String.`is`(where: Any?) = asWhere() `is` where // ==
|
||||
infix fun String.ne(where: Any?) = asWhere() ne where // !=
|
||||
infix fun String.lt(where: Any) = asWhere() lt where // <
|
||||
infix fun String.lte(where: Any) = asWhere() lte where // <=
|
||||
fun String.nin(vararg where: Any?) = asWhere() nin where // not contains
|
||||
infix fun String.nin(where: Collection<Any?>) = asWhere() nin where // not contains
|
||||
|
||||
infix fun String.all(where: Any?) = asWhere() all where
|
||||
infix fun String.all(where: Collection<Any?>) = asWhere() all where
|
||||
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)
|
||||
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()
|
||||
infix fun String.regex(regex: BsonRegularExpression) = asWhere() regex regex
|
||||
infix fun String.withinSphere(circle: Circle) = asWhere() withinSphere circle
|
||||
infix fun String.within(shape: Shape) = asWhere() within shape
|
||||
infix fun String.near(point: Point) = asWhere() near point
|
||||
infix fun String.nearSphere(point: Point) = asWhere() nearSphere point
|
||||
infix fun String.intersects(point: GeoJson<*>) = asWhere() intersects point
|
||||
infix fun String.maxDistance(maxDistance: Double) = asWhere() maxDistance maxDistance
|
||||
infix fun String.minDistance(maxDistance: Double) = asWhere() minDistance maxDistance
|
||||
infix fun String.elemMatch(c: QueryBuilder.() -> Unit) = asWhere() elemMatch c
|
||||
infix fun String.alike(sample: Example<*>) = asWhere() alike sample
|
||||
infix fun String.andDocumentStructureMatches(schema: MongoJsonSchema) = asWhere() andDocumentStructureMatches schema
|
||||
|
||||
infix fun String.equal(where: Any?) = asWhere() equal where
|
||||
infix fun String.eq(where: Any?) = asWhere() equal where
|
||||
|
||||
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.gte(where: Any) = gte(where).last(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.`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.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 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 infix fun Criteria.elemMatch(c: QueryBuilder.() -> Unit) =
|
||||
elemMatch(QueryBuilder criteria c).last(criteriaList::add)
|
||||
|
||||
private infix fun Criteria.alike(sample: Example<*>) = alike(sample).last(criteriaList::add)
|
||||
private infix fun Criteria.andDocumentStructureMatches(schema: MongoJsonSchema) =
|
||||
andDocumentStructureMatches(schema).last(criteriaList::add)
|
||||
|
||||
private infix fun Criteria.equal(where: Any?) = `is`(where).last(criteriaList::add)
|
||||
|
||||
/**
|
||||
* 根据 builder 所指定的规则排序
|
||||
*/
|
||||
infix fun Query.sort(builder: SortBuilder.() -> Unit) = with(SortBuilder(builder))
|
||||
|
||||
/**
|
||||
* 取以 field 为基准逆序排序的第一个元素
|
||||
*/
|
||||
infix fun Query.last(field: KProperty<*>) = apply {
|
||||
sort { field order Sort.Direction.DESC }
|
||||
limit(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取以所有 field 为基准逆序排序的第一个元素
|
||||
*/
|
||||
fun Query.last(vararg field: KProperty<*>) = last(field.iterator())
|
||||
|
||||
/**
|
||||
* 取以所有 field 为基准逆序排序的第一个元素
|
||||
*/
|
||||
@JvmName("lastFields")
|
||||
fun Query.last(fields: Iterable<KProperty<*>>) = last(fields.iterator())
|
||||
|
||||
/**
|
||||
* 取以所有 field 为基准逆序排序的第一个元素
|
||||
*/
|
||||
@JvmName("lastFields")
|
||||
fun Query.last(fields: Iterator<KProperty<*>>) = apply {
|
||||
sort { fields.forEach { it order Sort.Direction.DESC } }
|
||||
limit(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取以 field 为基准顺序排序的第一个元素
|
||||
*/
|
||||
infix fun Query.first(field: KProperty<*>) = apply {
|
||||
sort { field order Sort.Direction.ASC }
|
||||
limit(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取以所有 field 为基准顺序排序的第一个元素
|
||||
*/
|
||||
fun Query.first(vararg field: KProperty<*>) = first(field.iterator())
|
||||
|
||||
/**
|
||||
* 取以所有 field 为基准顺序排序的第一个元素
|
||||
*/
|
||||
@JvmName("firstFields")
|
||||
fun Query.first(fields: Iterable<KProperty<*>>) = first(fields.iterator())
|
||||
|
||||
/**
|
||||
* 取以所有 field 为基准顺序排序的第一个元素
|
||||
*/
|
||||
@JvmName("firstFields")
|
||||
fun Query.first(fields: Iterator<KProperty<*>>) = apply {
|
||||
sort { fields.forEach { it order Sort.Direction.ASC } }
|
||||
limit(1)
|
||||
}
|
||||
|
||||
infix fun Query.last(field: String) = apply {
|
||||
sort { field order Sort.Direction.DESC }
|
||||
limit(1)
|
||||
}
|
||||
|
||||
fun Query.last(vararg field: String) = last(field.iterator())
|
||||
fun Query.last(fields: Iterable<String>) = last(fields.iterator())
|
||||
fun Query.last(fields: Iterator<String>) = apply {
|
||||
sort { fields.forEach { it order Sort.Direction.DESC } }
|
||||
limit(1)
|
||||
}
|
||||
|
||||
infix fun Query.first(field: String) = apply {
|
||||
sort { field order Sort.Direction.ASC }
|
||||
limit(1)
|
||||
}
|
||||
|
||||
fun Query.first(vararg field: String) = first(field.iterator())
|
||||
fun Query.first(fields: Iterable<String>) = first(fields.iterator())
|
||||
fun Query.first(fields: Iterator<String>) = apply {
|
||||
sort { fields.forEach { it order Sort.Direction.ASC } }
|
||||
limit(1)
|
||||
}
|
||||
|
||||
val Query.fields get() = fields()
|
||||
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)
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.mongodb.core.aggregation.SortOperation
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||
class SortBuilder : MongoName, BsonConverter {
|
||||
companion object {
|
||||
operator fun invoke(action: SortBuilder.() -> Unit): Sort {
|
||||
val builder = SortBuilder()
|
||||
builder.action()
|
||||
return builder.sort!!
|
||||
}
|
||||
}
|
||||
|
||||
private var sort: Sort? = null
|
||||
val sortOperation get() = SortOperation(sort!!)
|
||||
|
||||
operator fun invoke(action: SortBuilder.() -> Unit) = this.action()
|
||||
|
||||
infix fun String.order(direction: Sort.Direction): Sort = Sort.by(Sort.Order(direction, this)).apply {
|
||||
updateSort(this) { this.and(it) }
|
||||
}
|
||||
|
||||
infix fun KProperty<*>.order(direction: Sort.Direction) = mongoName order direction
|
||||
|
||||
fun by(property: String) = Sort.by(property).apply {
|
||||
updateSort(this) { this.and(it) }
|
||||
}
|
||||
|
||||
fun by(property: KProperty<*>) = by(property.mongoName)
|
||||
|
||||
private fun updateSort(sort: Sort, action: (Sort) -> Sort) {
|
||||
if (this.sort == null) {
|
||||
this.sort = sort
|
||||
} else {
|
||||
this.sort = action(this.sort!!)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import org.bson.Document
|
||||
import org.springframework.data.mongodb.core.query.Update
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "HasPlatformType")
|
||||
class UpdateBuilder(val update: Update = Update()) : MongoName, BsonConverter {
|
||||
companion object {
|
||||
inline infix operator fun invoke(operator: UpdateBuilder.() -> Unit): Update =
|
||||
UpdateBuilder(Update()).also(operator).update
|
||||
|
||||
inline infix fun updateObject(operator: UpdateBuilder.() -> Unit): Document =
|
||||
UpdateBuilder(Update()).also(operator).update.updateObject
|
||||
|
||||
private val addMultiFieldOperationMethod = Update::class.java.getDeclaredMethod(
|
||||
"addMultiFieldOperation",
|
||||
String::class.java,
|
||||
String::class.java,
|
||||
Any::class.java
|
||||
)
|
||||
|
||||
init {
|
||||
addMultiFieldOperationMethod.isAccessible = true
|
||||
}
|
||||
|
||||
private fun Update.addMultiFieldOperation(operator: String, key: String, value: Any?) {
|
||||
addMultiFieldOperationMethod.invoke(this, operator, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
infix fun String.set(value: Any?) = update.set(this, value?.bsonValue())
|
||||
infix fun String.setOnInsert(value: Any?) = update.setOnInsert(this, value?.bsonValue())
|
||||
|
||||
fun String.unset() = update.unset(this)
|
||||
infix fun String.inc(inc: Number) = update.inc(this, inc)
|
||||
fun String.inc() = update.inc(this, 1)
|
||||
infix fun String.push(value: Any?) = update.push(this, value?.bsonValue())
|
||||
fun String.push() = update.push(this)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated(
|
||||
"as of MongoDB 2.4. Removed in MongoDB 3.6. Use {@link #push(String) \$push \$each} instead.",
|
||||
ReplaceWith("push")
|
||||
)
|
||||
infix fun String.pushAll(values: Array<out Any?>) = update.pushAll(this, values)
|
||||
|
||||
fun String.addToSet() = update.addToSet(this)
|
||||
infix fun String.addToSet(value: Any?) = update.addToSet(this, value?.bsonValue())
|
||||
infix fun String.pop(pos: Update.Position) = update.pop(this, pos)
|
||||
infix fun String.pull(value: Any?) = update.pull(this, value?.bsonValue())
|
||||
infix fun String.pull(query: QueryBuilder.() -> Unit) = update.pull(this, QueryBuilder queryObject query)
|
||||
infix fun String.pullAll(values: Array<out Any?>) = update.pullAll(this, values)
|
||||
infix fun String.pullAll(values: Iterable<Any?>) = update.addMultiFieldOperation("\$pullAll", this, values)
|
||||
infix fun String.rename(newName: String) = update.rename(this, newName)
|
||||
fun String.currentDate() = update.currentDate(this)
|
||||
fun String.currentTimestamp() = update.currentTimestamp(this)
|
||||
infix fun String.multiply(multiplier: Number) = update.multiply(this, multiplier)
|
||||
infix fun String.max(value: Any) = update.max(this, value.bsonValue())
|
||||
infix fun String.min(value: Any) = update.min(this, value.bsonValue())
|
||||
fun String.bitwise() = update.bitwise(this)
|
||||
fun String.modifies() = update.modifies(this)
|
||||
|
||||
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
|
||||
fun KProperty<*>.unset() = mongoName.unset()
|
||||
infix fun KProperty<*>.inc(inc: Number) = mongoName inc inc
|
||||
fun KProperty<*>.inc() = mongoName inc (1)
|
||||
infix fun KProperty<*>.push(value: Any) = mongoName push value
|
||||
fun KProperty<*>.push() = mongoName.push()
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated(
|
||||
"as of MongoDB 2.4. Removed in MongoDB 3.6. Use {@link #push(String) \$push \$each} instead.",
|
||||
ReplaceWith("push")
|
||||
)
|
||||
infix fun <T> KProperty<T>.pushAll(values: Array<T>) = mongoName pushAll values
|
||||
|
||||
fun KProperty<*>.addToSet() = mongoName.addToSet()
|
||||
infix fun <T : Any> KProperty<Iterable<T>>.addToSet(value: T) = mongoName addToSet value
|
||||
|
||||
@JvmName("addToSetArray")
|
||||
infix fun <T> KProperty<Array<T>>.addToSet(value: T) = mongoName addToSet value
|
||||
infix fun KProperty<*>.pop(pos: Update.Position) = mongoName pop pos
|
||||
infix fun <T> KProperty<Iterable<T>>.pull(value: T) = mongoName pull value
|
||||
|
||||
@JvmName("pullArray")
|
||||
infix fun <T> KProperty<Array<T>>.pull(value: T) = mongoName pull value
|
||||
infix fun KProperty<*>.pull(query: QueryBuilder.() -> Unit) =
|
||||
update.pull(mongoName, QueryBuilder queryObject query)
|
||||
|
||||
infix fun <T> KProperty<Array<T>>.pullAll(values: Array<out T>) = mongoName pullAll values
|
||||
infix fun <T> KProperty<Array<T>>.pullAll(values: Iterable<T>) = mongoName pullAll values
|
||||
|
||||
@JvmName("pullAllIterable")
|
||||
infix fun <T> KProperty<Iterable<T>>.pullAll(values: Array<out T>) = mongoName pullAll values
|
||||
|
||||
@JvmName("pullAllIterable")
|
||||
infix fun <T> KProperty<Iterable<T>>.pullAll(values: Iterable<T>) = mongoName pullAll values
|
||||
infix fun KProperty<*>.rename(newName: String) = mongoName rename newName
|
||||
fun KProperty<*>.currentDate() = mongoName.currentDate()
|
||||
fun KProperty<*>.currentTimestamp() = mongoName.currentTimestamp()
|
||||
infix fun KProperty<*>.multiply(multiplier: Number) = mongoName multiply multiplier
|
||||
infix fun KProperty<*>.max(value: Any) = mongoName max value
|
||||
infix fun KProperty<*>.min(value: Any) = mongoName min value
|
||||
fun KProperty<*>.bitwise() = mongoName.bitwise()
|
||||
fun KProperty<*>.modifies() = mongoName.modifies()
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import cn.tursom.core.uncheckedCast
|
||||
import com.mongodb.client.model.Collation
|
||||
import com.mongodb.client.model.UpdateOptions
|
||||
import org.bson.conversions.Bson
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||
class UpdateOption {
|
||||
companion object {
|
||||
operator fun invoke(builder: UpdateOption.() -> Unit): UpdateOptions = UpdateOption().apply(builder).updateOptions
|
||||
}
|
||||
|
||||
val updateOptions = UpdateOptions()
|
||||
var arrayFilters: MutableList<Bson>?
|
||||
@JvmName("arrayFiltersGetter")
|
||||
get() = updateOptions.arrayFilters.uncheckedCast()
|
||||
set(value) {
|
||||
updateOptions.arrayFilters(value)
|
||||
}
|
||||
|
||||
fun getArrayFilters(): MutableList<Bson> {
|
||||
var list = arrayFilters
|
||||
if (list == null) {
|
||||
list = ArrayList()
|
||||
arrayFilters = list
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun arrayFilter(builder: QueryBuilder.() -> Unit) = getArrayFilters().add(QueryBuilder queryObject builder)
|
||||
|
||||
var upsert: Boolean
|
||||
get() = updateOptions.isUpsert
|
||||
set(value) {
|
||||
updateOptions.upsert(value)
|
||||
}
|
||||
var bypassDocumentValidation: Boolean?
|
||||
get() = updateOptions.bypassDocumentValidation
|
||||
set(value) {
|
||||
updateOptions.bypassDocumentValidation(value)
|
||||
}
|
||||
var collation: Collation?
|
||||
get() = updateOptions.collation
|
||||
set(value) {
|
||||
updateOptions.collation(value)
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package cn.tursom.database.mongodb.spring.function
|
||||
|
||||
fun interface Function1<R, T> : SerializedFunction {
|
||||
operator fun T.invoke(): R
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package cn.tursom.database.mongodb.spring.function
|
||||
|
||||
import java.io.Serializable
|
||||
import java.lang.invoke.SerializedLambda
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
|
||||
interface SerializedFunction : Serializable
|
||||
|
||||
private val getterRegex = Regex("^get[A-Z].*")
|
||||
private val booleanGetterRegex = Regex("^is[A-Z].*")
|
||||
|
||||
val SerializedFunction.serializedLambda: SerializedLambda
|
||||
get() {
|
||||
val writeReplace = javaClass.getDeclaredMethod("writeReplace")
|
||||
writeReplace.isAccessible = true
|
||||
return writeReplace.invoke(this) as SerializedLambda
|
||||
}
|
||||
|
||||
val SerializedFunction.implClassName: String get() = serializedLambda.implClass.replace('/', '.')
|
||||
|
||||
val SerializedFunction.implClass: Class<*> get() = Class.forName(implClassName)
|
||||
val SerializedFunction.implMethodName: String get() = serializedLambda.implMethodName
|
||||
|
||||
val SerializedFunction.implMethod: Method get() = implClass.getMethod(serializedLambda.implMethodName)
|
||||
val SerializedFunction.fieldName: String?
|
||||
get() {
|
||||
val methodName = implMethodName
|
||||
return getFieldNameFromGetterName(methodName)
|
||||
}
|
||||
|
||||
val SerializedFunction.field: Field?
|
||||
get() {
|
||||
val fieldName = fieldName ?: return null
|
||||
val clazz = implClass
|
||||
return clazz.getDeclaredField(fieldName)
|
||||
}
|
||||
|
||||
fun getFieldNameFromGetterName(getterName: String): String? = when {
|
||||
getterRegex.matches(getterName) -> {
|
||||
val chars = getterName.toCharArray()
|
||||
chars[3] = Character.toLowerCase(chars[3])
|
||||
String(chars, 3, chars.size - 3)
|
||||
}
|
||||
booleanGetterRegex.matches(getterName) -> {
|
||||
val chars = getterName.toCharArray()
|
||||
chars[2] = Character.toLowerCase(chars[2])
|
||||
String(chars, 2, chars.size - 2)
|
||||
}
|
||||
else -> getterName
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
import com.mongodb.client.result.DeleteResult
|
||||
import com.mongodb.client.result.UpdateResult
|
||||
import org.springframework.data.mongodb.core.MongoTemplate
|
||||
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
|
||||
|
||||
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,
|
||||
) = count(QueryBuilder(operator), T::class.java)
|
||||
|
||||
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,
|
||||
): List<T> = find(QueryBuilder(operator), T::class.java)
|
||||
|
||||
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,
|
||||
): 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.upsert(
|
||||
query: Query,
|
||||
update: Update,
|
||||
): UpdateResult = upsert(query, 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)
|
||||
|
||||
inline fun <reified T : Any> MongoTemplate.updateFirst(
|
||||
query: Query,
|
||||
update: Update,
|
||||
): UpdateResult = updateFirst(query, 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)
|
||||
|
||||
inline fun <reified T : Any> MongoTemplate.updateMulti(
|
||||
query: Query,
|
||||
update: Update,
|
||||
): UpdateResult = updateMulti(query, 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)
|
||||
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
|
||||
inline fun <reified T : Any> MongoTemplate.remove(
|
||||
queryBuilder: QueryBuilder.() -> Unit,
|
||||
): DeleteResult = remove(QueryBuilder(queryBuilder), T::class.java)
|
||||
|
||||
infix fun Query.sort(builder: SortBuilder.() -> Unit) {
|
||||
with(SortBuilder(builder))
|
||||
}
|
||||
|
||||
infix fun Field.include(key: KProperty<*>) = include(MongoName.mongoName(key))
|
||||
infix fun Field.exclude(key: KProperty<*>) = exclude(MongoName.mongoName(key))
|
||||
fun Field.slice(key: KProperty<*>, size: Int) = slice(MongoName.mongoName(key), size)
|
||||
fun Field.slice(key: KProperty<*>, offset: Int, size: Int) = slice(MongoName.mongoName(key), offset, size)
|
||||
fun Field.elemMatch(key: KProperty<*>, elemMatchCriteria: Criteria) =
|
||||
elemMatch(MongoName.mongoName(key), elemMatchCriteria)
|
||||
|
||||
fun Field.position(field: KProperty<*>, value: Int) = position(MongoName.mongoName(field), value)
|
@ -0,0 +1,13 @@
|
||||
package cn.tursom.database.mongodb.spring
|
||||
|
||||
class UpdateBuilderTest {
|
||||
data class Test(val a: String, val b: Int)
|
||||
|
||||
@org.junit.Test
|
||||
fun testPush() {
|
||||
println(UpdateBuilder {
|
||||
Test::a push Test("a", 1)
|
||||
Test::b set 1
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user