MongoDB Tools API - lifedever/kt-speedy-toolbox GitHub Wiki
kt-speedy-data-mongo 模块提供了 Spring Data MongoDB 的增强工具,包括基础实体类、服务层基类和丰富的查询操作。
提供了 MongoDB 基础实体类,包含审计字段和删除标记。
open class MongoSupportModel : Serializable
字段说明:
-
id: ObjectId
- MongoDB 主键,自动生成 -
createdDate: Date?
- 创建时间,自动设置 -
lastModifiedDate: Date?
- 最后修改时间,自动更新 -
createdBy: String?
- 创建人 -
lastModifiedBy: String?
- 最后修改人 -
deleted: Boolean
- 删除标记,默认false
使用示例:
@Document(collection = "users")
class User : MongoSupportModel() {
var username: String? = null
var email: String? = null
var phone: String? = null
var profile: UserProfile? = null
}
data class UserProfile(
val firstName: String,
val lastName: String,
val avatar: String?
)
提供了 MongoDB 通用服务层基类,包含丰富的查询和操作方法。
abstract class MongoSupportService<T : MongoSupportModel>(private val clazz: Class<T>)
必须实现:
protected abstract val template: MongoTemplate
根据ID查询单个文档。
fun findById(id: ObjectId): T?
fun findById(id: ObjectId, fields: List<String>): T? // 指定返回字段
示例:
val user = userService.findById(ObjectId("507f1f77bcf86cd799439011"))
val userBasic = userService.findById(ObjectId("507f1f77bcf86cd799439011"), listOf("username", "email"))
根据字段值查询单个文档。
fun getOneByFieldIs(field: String, isValue: Any?): T?
示例:
val user = userService.getOneByFieldIs("username", "john_doe")
val user2 = userService.getOneByFieldIs("email", "[email protected]")
根据字段值查询多个文档。
fun findByFieldIs(field: String, isValue: Any?): List<T>
fun findByFieldIs(field: String, isValue: Any?, sort: Sort): List<T>
fun findByFieldIs(field: String, isValue: Any?, pageable: Pageable): Page<T>
示例:
val activeUsers = userService.findByFieldIs("status", "active")
val sortedUsers = userService.findByFieldIs("department", "IT", Sort.by("createdDate").descending())
val pagedUsers = userService.findByFieldIs("role", "admin", PageRequest.of(0, 10))
根据字段值在指定集合中查询。
fun findByFieldIn(field: String, values: Collection<Any>): List<T>
fun findByFieldIn(field: String, values: Collection<Any>, sort: Sort): List<T>
示例:
val userIds = listOf("user1", "user2", "user3")
val users = userService.findByFieldIn("userId", userIds)
使用 Query 对象进行复杂查询。
fun findByQuery(query: Query): List<T>
fun findByQuery(query: Query, pageable: Pageable): Page<T>
fun getOneByQuery(query: Query): T?
示例:
val query = Query()
.addCriteria(Criteria.where("age").gte(18).lte(65))
.addCriteria(Criteria.where("status").`is`("active"))
.with(Sort.by("createdDate").descending())
val adults = userService.findByQuery(query)
使用正则表达式查询。
fun findByRegex(field: String, regex: String): List<T>
fun findByRegex(field: String, regex: String, options: String): List<T>
示例:
// 查找用户名以 "admin" 开头的用户
val adminUsers = userService.findByRegex("username", "^admin.*")
// 不区分大小写的搜索
val users = userService.findByRegex("email", ".*@gmail\\.com$", "i")
根据ID更新单个字段。
fun updateFieldById(id: ObjectId, key: String, value: Any?): T
fun unsetFieldById(id: ObjectId, key: String): T
示例:
// 更新用户状态
val updatedUser = userService.updateFieldById(userId, "status", "inactive")
// 删除字段
val user = userService.unsetFieldById(userId, "temporaryField")
根据ID使用 Update 对象更新。
fun updateById(id: ObjectId, update: Update): T
示例:
val update = Update()
.set("lastLoginDate", Date())
.inc("loginCount", 1)
.addToSet("tags", "frequent_user")
val updatedUser = userService.updateById(userId, update)
根据查询条件批量更新。
fun updateByQuery(query: Query, update: Update): UpdateResult
fun updateFirstByQuery(query: Query, update: Update): UpdateResult
示例:
val query = Query.query(Criteria.where("status").`is`("pending"))
val update = Update().set("status", "processed").set("processedDate", Date())
val result = userService.updateByQuery(query, update)
println("更新了 ${result.modifiedCount} 个文档")
统计文档数量。
fun count(): Long
fun count(query: Query): Long
示例:
val totalUsers = userService.count()
val activeUsers = userService.count(Query.query(Criteria.where("status").`is`("active")))
获取字段的不重复值。
fun distinct(field: String): List<Any>
fun distinct(field: String, query: Query): List<Any>
示例:
val departments = userService.distinct("department")
val activeDepartments = userService.distinct("department",
Query.query(Criteria.where("status").`is`("active")))
执行聚合查询。
fun aggregate(aggregation: Aggregation): AggregationResults<T>
fun <R> aggregate(aggregation: Aggregation, resultClass: Class<R>): AggregationResults<R>
示例:
// 按部门统计用户数量
val aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("status").`is`("active")),
Aggregation.group("department").count().`as`("userCount"),
Aggregation.sort(Sort.Direction.DESC, "userCount")
)
val results = userService.aggregate(aggregation, DepartmentStats::class.java)
data class DepartmentStats(
val _id: String, // department
val userCount: Long
)
根据ID删除文档。
fun deleteById(id: ObjectId)
fun remove(t: T): DeleteResult
fun remove(query: Query): DeleteResult
示例:
userService.deleteById(userId)
val query = Query.query(Criteria.where("status").`is`("inactive"))
val result = userService.remove(query)
逻辑删除(设置deleted字段为true)。
fun deleteByIdLogic(id: ObjectId)
示例:
userService.deleteByIdLogic(userId) // 只是标记删除,不物理删除
@Service
class UserService : MongoSupportService<User>(User::class.java) {
@Autowired
override lateinit var template: MongoTemplate
fun createUser(username: String, email: String, profile: UserProfile): User {
val user = User().apply {
this.username = username
this.email = email
this.profile = profile
}
return save(user)
}
fun findActiveUsersByDepartment(department: String): List<User> {
val query = Query()
.addCriteria(Criteria.where("department").`is`(department))
.addCriteria(Criteria.where("status").`is`("active"))
.addCriteria(Criteria.where("deleted").`is`(false))
return findByQuery(query)
}
fun searchUsersByKeyword(keyword: String, page: Int, size: Int): Page<User> {
val regex = ".*$keyword.*"
val query = Query()
.addCriteria(
Criteria().orOperator(
Criteria.where("username").regex(regex, "i"),
Criteria.where("email").regex(regex, "i"),
Criteria.where("profile.firstName").regex(regex, "i"),
Criteria.where("profile.lastName").regex(regex, "i")
)
)
.addCriteria(Criteria.where("deleted").`is`(false))
val pageable = PageRequest.of(page, size, Sort.by("createdDate").descending())
return findByQuery(query, pageable)
}
fun updateUserLastLogin(userId: ObjectId): User {
val update = Update()
.set("lastLoginDate", Date())
.inc("loginCount", 1)
return updateById(userId, update)
}
fun getUserStatsByDepartment(): List<DepartmentStats> {
val aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("deleted").`is`(false)),
Aggregation.group("department")
.count().`as`("totalUsers")
.sum(ConditionalOperators.`when`(Criteria.where("status").`is`("active")).then(1).otherwise(0))
.`as`("activeUsers"),
Aggregation.sort(Sort.Direction.DESC, "totalUsers")
)
return aggregate(aggregation, DepartmentStats::class.java).mappedResults
}
fun batchUpdateUserStatus(userIds: List<ObjectId>, newStatus: String): Long {
val query = Query.query(Criteria.where("id").`in`(userIds))
val update = Update()
.set("status", newStatus)
.set("statusUpdatedDate", Date())
return updateByQuery(query, update).modifiedCount
}
}
data class DepartmentStats(
val _id: String, // department name
val totalUsers: Long,
val activeUsers: Long
)
@Service
class ArticleService : MongoSupportService<Article>(Article::class.java) {
@Autowired
override lateinit var template: MongoTemplate
fun searchArticles(
keyword: String?,
author: String?,
tags: List<String>?,
publishedAfter: Date?,
publishedBefore: Date?,
pageable: Pageable
): Page<Article> {
val criteria = mutableListOf<Criteria>()
// 关键词搜索
keyword?.let {
criteria.add(
Criteria().orOperator(
Criteria.where("title").regex(".*$it.*", "i"),
Criteria.where("content").regex(".*$it.*", "i")
)
)
}
// 作者筛选
author?.let {
criteria.add(Criteria.where("author").`is`(it))
}
// 标签筛选
tags?.takeIf { it.isNotEmpty() }?.let {
criteria.add(Criteria.where("tags").`in`(it))
}
// 发布时间范围
publishedAfter?.let {
criteria.add(Criteria.where("publishedDate").gte(it))
}
publishedBefore?.let {
criteria.add(Criteria.where("publishedDate").lte(it))
}
// 只查询已发布且未删除的文章
criteria.add(Criteria.where("published").`is`(true))
criteria.add(Criteria.where("deleted").`is`(false))
val query = Query()
if (criteria.isNotEmpty()) {
query.addCriteria(Criteria().andOperator(*criteria.toTypedArray()))
}
return findByQuery(query, pageable)
}
}
- ObjectId: MongoDB 使用 ObjectId 作为主键,需要正确处理类型转换
- 索引: 对于经常查询的字段建议创建索引以提高性能
- 聚合查询: 复杂的统计查询建议使用聚合管道
- 逻辑删除: 查询时记得过滤已删除的文档(deleted=false)
- 分页: 大数据量查询时建议使用分页避免内存问题