nbcp.myoql.db.mongo.component.MongoAggregateClip.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ktmyoql Show documentation
Show all versions of ktmyoql Show documentation
kotlin orm -- mysql,mongo , just like ktorm
The newest version!
package nbcp.myoql.db.mongo
import nbcp.base.comm.JsonMap
import nbcp.base.comm.ListResult
import nbcp.base.comm.MyRawString
import nbcp.base.comm.config
import nbcp.base.enums.JsonSceneScopeEnum
import nbcp.base.extend.*
import nbcp.base.extend.*
import nbcp.base.extend.*
import nbcp.base.utils.Md5Util
import nbcp.myoql.db.db
import nbcp.myoql.db.enums.MyOqlDbScopeEnum
import nbcp.myoql.db.mongo.base.MongoColumnName
import nbcp.myoql.db.mongo.component.MongoAggregateBeginMatch
import nbcp.myoql.db.mongo.component.MongoBaseMetaCollection
import nbcp.myoql.db.mongo.component.MongoClipBase
import nbcp.myoql.db.mongo.enums.PipeLineEnum
import nbcp.myoql.db.mongo.extend.procWithMongoScript
import nbcp.myoql.db.mongo.extend.toOIdJson
import nbcp.myoql.db.mongo.logger.logFind
import org.bson.Document
import org.bson.types.ObjectId
import org.slf4j.LoggerFactory
import org.springframework.data.mongodb.core.query.Criteria
import java.time.LocalDateTime
/** https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/ */
/**
* MongoAggregate
*/
class MongoAggregateClip, E : Any>(var moerEntity: M) :
MongoClipBase(moerEntity.tableName) {
companion object {
private val logger = LoggerFactory.getLogger(this::class.java.declaringClass);
}
val pipeLines = mutableListOf>();
private var skip: Int = 0;
private var take: Int = -1;
/**
* 通用函数
*/
fun addPipeLine(key: PipeLineEnum, jsonCallback: (M) -> Map): MongoAggregateClip {
return addPipeLine(key, jsonCallback.invoke(this.moerEntity));
}
/**
* 通用函数
*/
fun addPipeLine(key: PipeLineEnum, json: Map): MongoAggregateClip {
this.pipeLines.add("\$${key.key}" to json);
return this;
}
/**
* @param rawString 原始的mongo字符串,不解析
*/
fun addPipeLineRawString(key: PipeLineEnum, rawString: String): MongoAggregateClip {
this.pipeLines.add("\$${key.key}" to MyRawString(rawString));
return this;
}
/**
* 对当前表递归,并返回 wbs
* @see https://www.mongodb.com/zh-cn/docs/manual/reference/operator/aggregation/graphLookup/
* @param connectFromField 当前表From字段,一般是 parent._id
* @param connectToField 当前表To字段,一般是 _id
*/
fun addGraphLookup(
connectFromField: (M) -> MongoColumnName,
connectToField: (M) -> MongoColumnName,
alias: String = "wbs"
): MongoAggregateClip {
var jsonMap = JsonMap();
var connectFromField2 = db.mongo.getMongoColumnName(connectFromField(this.moerEntity).toString());
jsonMap.put("from", this.moerEntity.tableName)
jsonMap.put("startWith", "$" + connectFromField2)
jsonMap.put("connectFromField", connectFromField2)
jsonMap.put("connectToField", db.mongo.getMongoColumnName(connectToField(this.moerEntity).toString()))
jsonMap.put("as", alias)
this.pipeLines.add("\$${PipeLineEnum.GRAPH_LOOKUP.key}" to jsonMap);
return this;
}
fun skip(skip: Int): MongoAggregateClip {
this.skip = skip;
this.pipeLines.add("\$skip" to skip);
return this;
}
fun take(take: Int): MongoAggregateClip {
this.take = take;
this.pipeLines.add("\$limit" to take);
return this;
}
fun limit(skip: Int, take: Int): MongoAggregateClip {
return this.skip(skip).take(take)
}
fun wheres(vararg whereDatas: Criteria): MongoAggregateClip {
if (whereDatas.any() == false) return this;
pipeLines.add("\$match" to db.mongo.getMergedMongoCriteria(*whereDatas))
return this;
}
fun wheres(vararg wheres: (M) -> Criteria): MongoAggregateClip {
return wheres(*wheres.map { it(moerEntity) }.toTypedArray());
}
/**开始收集where条件
*/
fun beginMatch(): MongoAggregateBeginMatch {
return MongoAggregateBeginMatch(this)
}
fun select(vararg columns: String): MongoAggregateClip {
pipeLines.add("\$project" to columns.map { it to "\$${it}" }.toMap());
return this;
}
fun select(column: (M) -> MongoColumnName): MongoAggregateClip {
return select(column(moerEntity).toString());
}
fun count(columnName: String): MongoAggregateClip {
pipeLines.add("\$count" to columnName)
return this;
}
fun unset(vararg columns: String): MongoAggregateClip {
pipeLines.add("\$unset" to columns)
return this;
}
/**
* @param group:
*
* {
* "列名1": { "聚合函数": "列名"} ,
* "列名2": { "聚合函数": "列名"} ,
* "列名3": { "聚合函数": "列名"}
* }
*
* 其中, 列名1,列名2,列名3,必须有一个是 _id
*/
fun rawGroup(group: (M) -> Map): MongoAggregateClip {
var raw = group.invoke(this.moerEntity)
pipeLines.add("\$group" to raw)
return this;
}
fun rawGroup(group: Map): MongoAggregateClip {
pipeLines.add("\$group" to group)
return this;
}
/**
* @param _id: 如果要设置列,前面加$.
* @param eachItems: 每一个聚合的表达式。
* @see MongoExpression
*/
fun groupWithId(_id: String, eachItems: Map): MongoAggregateClip {
var raw = JsonMap();
raw.put("_id", _id)
raw.putAll(eachItems)
pipeLines.add("\$group" to raw)
return this;
}
/**
* @param _id: 如果要设置列,前面加$. 如: { $group: {"_id": { code: "$productLine.code" } } }
* @param eachItems: 每一个聚合的表达式。
* @see MongoExpression
*/
@JvmOverloads
fun groupWithId(_id: Map, vararg eachItems: Map): MongoAggregateClip {
var raw = JsonMap();
raw.put("_id", _id)
eachItems.forEach {
raw.putAll(it)
}
pipeLines.add("\$group" to raw)
return this;
}
/**
* @param sortFuncs: true:正序, false,逆序。
*/
fun orderBy(vararg sortFuncs: Pair): MongoAggregateClip {
var sorts = sortFuncs.map {
var sortName = db.mongo.getMongoColumnName(it.first)
return@map sortName to (if (it.second) 1 else -1)
}
pipeLines.add("\$sort" to JsonMap(sorts.toList()))
return this;
}
// fun orderBy(vararg sortFuncs: (M) -> MongoOrderBy): MongoAggregateClip {
// return orderBy(*sortFuncs.map {
// var order = it(moerEntity);
// return@map order.orderBy.toString() to order.Asc
// }.toTypedArray());
// }
fun toExpression(): String {
var pipeLines = mutableListOf>();
pipeLines.addAll(this.pipeLines);
var pipeLineExpression = "[" + pipeLines.map {
var key = it.first;
var value = it.second;
if (value is ObjectId) {
return@map """{$key:${value.toString().toOIdJson().ToJson(JsonSceneScopeEnum.DB).AsString("null")}}"""
} else if (value is Criteria) {
var c_value = value.criteriaObject //.procWithMongoScript();
return@map """{$key:${c_value.toJson().AsString("null")}}"""
} else if (value is Document) {
return@map """{$key:${value.toJson().AsString("null")}}"""
} else if (value is Number || value is MyRawString) {
return@map "{$key:$value}";
} else if (value is String) {
// if( key == "_id" || key.endsWith("._id")){
// return@map """{$key:{##oid:"${value}"}}""".replace("##","$")
// }
return@map """{$key:"${value}"}"""
} else if (value is Map<*, *>) {
return@map "{$key:${value.procWithMongoScript().ToJson(JsonSceneScopeEnum.DB).AsString("null")}}"
}
logger.warn("不识别的类型:${value::class.java.name}")
return@map "{$key:${value.ToJson(JsonSceneScopeEnum.DB).AsString("null")}}"
}.joinToString(",") + "]"
var exp = """{
aggregate: "${actualTableName}",
pipeline: ${pipeLineExpression} ,
cursor: {} } """
return exp;
}
/**
* 返回该对象的 Md5。
*/
private fun getCacheKey(): String {
var exp = toExpression();
return Md5Util.getBase64Md5(exp);
}
fun toList(itemFunc: ((Document) -> Unit)? = null): MutableList {
return toList(this.moerEntity.entityClass, itemFunc);
}
@JvmOverloads
fun toList(type: Class, itemFunc: ((Document) -> Unit)? = null): MutableList {
return toMapList(itemFunc).map { it.ConvertJson(type) }.toMutableList()
}
/**
* 核心函数
*/
@JvmOverloads
fun toMapList(itemFunc: ((Document) -> Unit)? = null): MutableList {
db.affectRowCount = -1;
var queryJson = toExpression();
val settingResult = db.mongo.mongoEvents.onAggregate(this)
if (settingResult.any { it.result.result == false }) {
return mutableListOf();
}
var result: Document? = null
var startAt = LocalDateTime.now();
var error: Exception? = null;
try {
this.script = queryJson;
result =
getMongoTemplate(settingResult.lastOrNull { it.result.dataSource.HasValue }?.result?.dataSource)
.executeCommand(
queryJson
)
this.executeTime = LocalDateTime.now() - startAt
usingScope(arrayOf(MyOqlDbScopeEnum.IGNORE_AFFECT_ROW, MyOqlDbScopeEnum.IGNORE_EXECUTE_TIME)) {
settingResult.forEach {
it.event.aggregate(this, it.result)
}
}
} catch (e: Exception) {
error = e;
throw e;
} finally {
logger.logFind(error, actualTableName, queryJson, result);
// logger.InfoError(result == null) {
// """[aggregate] ${this.moerEntity.tableName}
//[语句] ${queryJson}
//${if (config.debug) "[result] ${result?.ToJson()}" else "[result.size] ${result?.size}"}
//[耗时] ${db.executeTime}"""
// }
}
if (result == null) {
throw RuntimeException("mongo aggregate执行错误!")
}
if (result.containsKey("ok") == false) {
throw RuntimeException("mongo aggregate执行错误!" + result.ToJson())
}
var ret = mutableListOf()
if (result.getDouble("ok") != 1.0) {
this.affectRowCount = result.getDouble("ok").AsInt()
return ret
}
var list = ((result.get("cursor") as Document).get("firstBatch") as ArrayList);
this.affectRowCount = list.size;
list.forEach {
// db.change_id2Id(it);
//value 可能会是: Document{{answerRole=Patriarch}}
MongoDocument2EntityUtil.procDocumentJson(it);
if (itemFunc != null) {
itemFunc(it);
}
ret.add(it)
}
return ret
}
@JvmOverloads
fun toMap(itemFunc: ((Document) -> Unit)? = null): Document {
this.take(1);
var ret = toMapList(itemFunc);
if (ret.any() == false) return Document();
return ret.first();
}
fun toScalar(): Any? {
var doc = toMap();
if (doc.keys.any() == false) return null
return doc.get(doc.keys.last())
}
/**
* 将忽略 skip , take
*/
fun count(): Int {
this.pipeLines.add("\$count" to "count");
return toScalar()?.AsInt() ?: 0
}
fun exists(): Boolean {
this.select("_id");
return toScalar()?.AsString().HasValue;
}
fun toList(): MutableList {
return toList(moerEntity.entityClass)
}
fun toListResult(itemFunc: ((Document) -> Unit)? = null): ListResult {
return toListResult(moerEntity.entityClass, itemFunc);
}
@JvmOverloads
fun toListResult(entityClass: Class, itemFunc: ((Document) -> Unit)? = null): ListResult {
var ret = ListResult()
var data = toList(entityClass, itemFunc)
if (config.listResultWithCount) {
ret.total = count()
} else if (this.skip == 0 && this.take > 0) {
if (data.size < this.take) {
ret.total = data.size;
} else {
ret.total = count()
}
}
ret.data = data;
return ret
}
fun toEntity(): E? {
this.take(1)
return toList(moerEntity.entityClass).firstOrNull();
}
@JvmOverloads
fun toEntity(type: Class, itemFunc: ((Document) -> Unit)? = null): R? {
this.take(1);
return toList(type, itemFunc).firstOrNull();
}
}