kv2doc 是一个高性能的嵌入式文档数据库,基于 Go 1.23+ 泛型、BoltDB 和 Expr-lang 实现。提供类型安全的 API 和优雅的查询体验。
go get cnb.cool/zishuo/kv2doc
要求:Go 1.23 或更高版本
package main
import (
"fmt"
"cnb.cool/zishuo/kv2doc"
)
// User 定义用户结构体
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
IsActive bool `json:"is_active"`
}
func main() {
// 创建数据库
db, _ := kv2doc.NewDB("demo.db")
defer db.Close()
// 方式1:传统 API(向后兼容)
// 适合:简单查询、动态字段、遗留系统
id, _ := db.Add("users", kv2doc.Doc{
"name": "张三",
"age": "25",
"is_active": "true",
})
docs, _ := db.Query("users").
Eq("is_active", "true").
Gt("age", "20").
List()
fmt.Printf("传统 API:找到 %d 个用户\n", len(docs))
// 方式2:类型安全 API(推荐日常使用)
// 适合:业务开发、需要类型检查、IDE友好
users := kv2doc.NewCollection[User](db, "users")
user := User{
Name: "李四",
Age: 30,
IsActive: true,
}
users.Insert(user)
activeUsers, _ := users.Query().
Eq("is_active", true).
Gt("age", 25).
Find()
for _, u := range activeUsers {
fmt.Printf("类型安全:%s (%d岁)\n", u.Data.Name, u.Data.Age)
}
// 方式3:表达式风格 API(最优雅)
// 适合:复杂查询、优雅语法、最佳可读性
results, _ := users.Expr().
WhereEq("is_active", true). // 简洁的等值查询
WhereBetween("age", 20, 35). // 范围查询
WhereIn("status", []string{"normal", "vip"}). // IN 查询
OrderByAsc("age"). // 排序
Page(1, 10). // 分页
Find()
fmt.Printf("表达式 API:找到 %d 个符合条件的用户\n", len(results))
// 💡 更多优雅用法
// 简化语法:Where("is_active", true) 等价于 WhereEq("is_active", true)
// 操作符语法:Where("age", ">", 25)
// OR 条件:WhereOr("role", "admin")
// 复杂组合:WhereGroup("OR", conditions...)
}
表达式风格 API 提供最优雅的语法,支持链式调用和丰富的查询条件:
users := kv2doc.NewCollection[User](db, "users")
// 🌟 基础优雅语法
results, _ := users.Expr().
WhereEq("is_active", true). // ✨ 等值查询
WhereBetween("age", 18, 65). // ✨ 范围查询
WhereIn("status", []string{"normal", "vip"}). // ✨ IN 查询
WhereLeftLike("name", "张"). // ✨ 前缀匹配
OrderByAsc("age"). // ✨ 排序
Page(1, 20). // ✨ 分页
Find()
// 🌟 简化语法(更简洁)
simple, _ := users.Expr().
Where("is_active", true). // 等价于 WhereEq
Where("age", ">", 25). // 带操作符
Find()
// 🌟 复杂逻辑组合
complex, _ := users.Expr().
WhereEq("is_active", true). // 活跃用户 AND
WhereOr("role", "admin"). // 或者是管理员
WhereBetween("age", 25, 60). // 年龄范围
OrderByDesc("created_at"). // 最新优先
Find()
// 🌟 分组条件 - 支持 (A AND B) OR (C AND D)
grouped, _ := users.Expr().
WhereGroup("OR",
kv2doc.And( // 活跃VIP用户
kv2doc.Eq("is_active", true),
kv2doc.Eq("status", "vip"),
),
kv2doc.And( // 或年轻管理员
kv2doc.Eq("role", "admin"),
kv2doc.Lt("age", 30),
),
).Find()
| 方法 | 说明 | 示例 | 索引优化 |
|---|---|---|---|
WhereEq(field, value) | 等于 | WhereEq("name", "张三") | ✅ 快速 |
Where(field, value) | 等于简写 | Where("age", 25) | ✅ 快速 |
Where(field, op, value) | 带操作符 | Where("age", ">", 18) | ❌ 扫描 |
WhereGt/Gte/Lt/Lte | 比较操作 | WhereGt("age", 18) | ❌ 扫描 |
WhereLike | 模糊匹配 | WhereLike("name", "张") | ❌ 扫描 |
WhereLeftLike | 前缀匹配 | WhereLeftLike("name", "张") | ✅ 快速 |
WhereIn/NotIn | 包含/排除 | WhereIn("status", []string{"active"}) | ✅ 快速 |
WhereBetween | 范围查询 | WhereBetween("age", 18, 65) | ❌ 扫描 |
WhereNull/NotNull | 空值检查 | WhereNotNull("email") | ❌ 扫描 |
WhereOr(field, value) | OR 条件 | WhereOr("role", "admin") | ❌ 扫描 |
// 1️⃣ 传统 API(完全向后兼容)
docs, _ := db.Query("users").Eq("is_active", "true").List()
// 2️⃣ 类型安全 API(常规业务推荐)
typedResults, _ := users.Query().Eq("is_active", true).Find()
// 3️⃣ 表达式 API(复杂查询推荐)
expressiveResults, _ := users.Expr().WhereEq("is_active", true).Find()
// 💡 三种API可以在同一项目中混用,根据场景选择最合适的
// ✅ 高性能查询(利用索引)
users.Expr().WhereEq("email", "user@example.com") // 精确匹配
users.Expr().WhereLeftLike("name", "张") // 前缀匹配
users.Expr().WhereIn("status", []string{"active"}) // IN查询
// ⚠️ 需要优化的查询(全表扫描)
users.Expr().WhereLike("name", "三") // 中间匹配
users.Expr().WhereGt("age", 25) // 范围查询
// 💡 优化策略:将高选择性条件放在前面
users.Expr().
WhereEq("email", email). // 高选择性优先
WhereGt("age", 18). // 低选择性后置
Find()
package main
import (
"fmt"
"log"
"cnb.cool/zishuo/kv2doc"
)
// User 用户模型
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
IsActive bool `json:"is_active"`
Status string `json:"status"`
Role string `json:"role"`
Tags []string `json:"tags"`
}
func main() {
// 创建数据库
db, err := kv2doc.NewDB("app.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 创建类型安全的集合
users := kv2doc.NewCollection[User](db, "users")
// 插入测试数据
testUsers := []User{
{Name: "张三", Age: 25, IsActive: true, Status: "normal", Role: "user"},
{Name: "李四", Age: 30, IsActive: false, Status: "vip", Role: "admin"},
{Name: "王五", Age: 35, IsActive: true, Status: "normal", Role: "user"},
}
bulk := users.Bulk()
for _, user := range testUsers {
bulk.Insert(user)
}
ids, _ := bulk.Execute()
fmt.Printf("批量插入 %d 个用户\n", len(ids))
// 🔍 场景1:查找活跃的年轻用户
youngActives, _ := users.Expr().
WhereEq("is_active", true).
WhereLt("age", 30).
OrderByAsc("age").
Find()
fmt.Printf("\n活跃年轻用户 (%d人):\n", len(youngActives))
for _, u := range youngActives {
fmt.Printf(" - %s (%d岁)\n", u.Data.Name, u.Data.Age)
}
// 🔍 场景2:复杂业务查询
businessResults, _ := users.Expr().
WhereEq("is_active", true). // 活跃用户
WhereIn("status", []string{"normal", "vip"}). // 正常状态
WhereBetween("age", 20, 40). // 核心年龄段
WhereOr("role", "admin"). // 或管理员
OrderByDesc("age"). // 年龄降序
Page(1, 10). // 分页
Find()
fmt.Printf("\n业务查询结果 (%d人):\n", len(businessResults))
for _, u := range businessResults {
fmt.Printf(" - %s (%d岁, %s, %s)\n",
u.Data.Name, u.Data.Age, u.Data.Status, u.Data.Role)
}
// 📊 统计信息
total, _ := users.Count()
active, _ := users.Expr().WhereEq("is_active", true).Count()
vips, _ := users.Expr().WhereEq("status", "vip").Count()
fmt.Printf("\n📊 统计信息:\n")
fmt.Printf(" 总用户数: %d\n", total)
fmt.Printf(" 活跃用户: %d\n", active)
fmt.Printf(" VIP用户: %d\n", vips)
// 🐛 调试:查看生成的查询
query := users.Expr().
WhereEq("is_active", true).
WhereBetween("age", 25, 35)
fmt.Printf("\n🐛 调试SQL: %s\n", query.ToSQL())
}
| 场景 | 推荐 API | 理由 | 示例 |
|---|---|---|---|
| 🆕 新项目 | 表达式风格 | 最优雅,功能最强 | users.Expr().WhereEq("status", "active") |
| 💼 业务开发 | 类型安全 | 平衡性能与易用性 | users.Query().Eq("is_active", true) |
| 🔄 遗留项目 | 传统 API | 向后兼容,零迁移成本 | db.Query("users").Eq("status", "active") |
| 🔍 复杂查询 | 表达式风格 | 支持复杂逻辑组合 | users.Expr().WhereGroup("OR", ...) |
| ⚡ 简单查询 | 任意选择 | 三种API性能相当 | 根据团队偏好选择 |
🏗️ kv2doc 架构
│
├── 🔧 传统 API (v1.0) - 向后兼容
│ └── db.Query("table").Eq("field", "value")
│
├── 🛡️ 类型安全 API (v2.0) - 编译时检查
│ └── collection.Query().Eq("field", value)
│
└── ✨ 表达式风格 API (v3.0) - 最优雅
└── collection.Expr().WhereEq("field", value)
💡 演进建议:新功能使用表达式风格,现有代码保持不变
// 🔄 无缝迁移 - 三种API可在同一项目中混用
func searchUsers(db *kv2doc.DB) {
// 遗留代码保持不变
legacyResults, _ := db.Query("users").
Eq("status", "active").
List()
// 新代码使用类型安全
users := kv2doc.NewCollection[User](db, "users")
typedResults, _ := users.Query().
Eq("is_active", true).
Find()
// 复杂查询使用表达式风格
expressiveResults, _ := users.Expr().
WhereEq("is_active", true).
WhereOr("role", "admin").
Find()
// 💡 根据场景选择,无需一次性迁移
}
// 查询:活跃用户,年龄18-65岁,按年龄排序,取前10个
// 1️⃣ 传统 API
docs, _ := db.Query("users").
Eq("is_active", "true").
Gt("age", "18").
Lt("age", "65").
Asc("age").
Limit(0, 10).
List()
// 2️⃣ 类型安全 API
results, _ := users.Query().
Eq("is_active", true).
Gt("age", 18).
Lt("age", 65).
OrderBy("age", false).
Limit(0, 10).
Find()
// 3️⃣ 表达式风格 API(最优雅)
elegant, _ := users.Expr().
WhereEq("is_active", true).
WhereBetween("age", 18, 65). // 🌟 更简洁的范围查询
OrderByAsc("age"). // 🌟 语义更清晰
Limit(10). // 🌟 更简洁的分页
Find()
Collection 是推荐的主要接口,提供完整的类型安全操作:
// 🏗️ 创建集合
users := kv2doc.NewCollection[User](db, "users")
// ➕ 插入文档
id, err := users.Insert(User{
Name: "张三",
Age: 25,
IsActive: true,
})
// 🔍 查找文档
user, err := users.FindByID(id) // 根据ID查找
allUsers, err := users.Find() // 查找所有
// 🔄 更新文档
err = users.Update(id, User{Name: "张三更新", Age: 26})
// 🗑️ 删除文档
err = users.Delete(id)
// 📊 统计数量
count, err := users.Count()
// 1️⃣ 简单查询 - 快速上手
activeUsers, _ := users.Where("is_active", true).Find()
// 2️⃣ 类型安全查询 - 常规业务推荐
results, _ := users.Query().
Eq("is_active", true). // 等值条件
Gt("age", 18). // 大于条件
Like("name", "张"). // 模糊匹配
OrderBy("age", false). // 排序(false=升序)
Limit(0, 10). // 分页
Find()
// 3️⃣ 表达式风格查询 - 复杂条件最优雅 ⭐
elegantResults, _ := users.Expr().
WhereEq("is_active", true). // ✨ 语义清晰
WhereBetween("age", 18, 65). // ✨ 范围查询
WhereIn("status", []string{"normal", "vip"}). // ✨ IN 查询
WhereOr("role", "admin"). // ✨ OR 条件
OrderByDesc("created_at"). // ✨ 降序排序
Page(1, 20). // ✨ 分页
Find()
// 📄 分页查询
pagedUsers, _ := users.FindWithPaging(0, 10) // 偏移量, 页大小
// 📊 排序查询
sortedUsers, _ := users.FindSorted("age", false) // 字段, 是否降序
// 📄📊 排序+分页
results, _ := users.FindSortedWithPaging("name", true, 0, 5)
// 🎯 约束条件查询
qualifiedUsers, _ := users.FindWhere(
kv2doc.Equal[User]("is_active", true), // 类型安全约束
kv2doc.GreaterThan[User]("age", 18),
)
// 📊 条件统计
activeCount, _ := users.CountWhere(
kv2doc.Equal[User]("is_active", true),
)
// 🚀 创建批量操作
bulk := users.Bulk()
// ➕ 批量插入
newUsers := []User{
{Name: "李四", Age: 30, IsActive: true},
{Name: "王五", Age: 25, IsActive: false},
}
for _, user := range newUsers {
bulk.Insert(user) // 加入批量队列
}
// 🔄 批量更新
bulk.Update("1", User{Name: "李四(更新)", Age: 31})
// 🗑️ 批量删除
bulk.Delete("2")
// 💥 一次性执行所有操作
ids, err := bulk.Execute() // 事务性执行
fmt.Printf("批量操作完成,处理了 %d 条记录\n", len(ids))
适合需要跨表操作或精细控制的场景:
// 🏗️ 创建泛型数据库实例
userDB := kv2doc.NewTypedDB[User](db)
// 🎯 支持多表操作
id, _ := userDB.Insert("users", user)
id2, _ := userDB.Insert("admins", adminUser) // 同一类型,不同表
// 🔍 跨表查询
results, _ := userDB.Query("users").
Eq("is_active", true).
Find()
// 🔄 流式处理大数据集
err := userDB.Query("users").ForEach(func(user *kv2doc.TypedDoc[User]) bool {
fmt.Printf("处理用户: %s\n", user.Data.Name)
return true // 继续遍历
})
type CustomConverter struct{}
func (c *CustomConverter) ToString(v any) string {
switch val := v.(type) {
case time.Time:
return val.Format(time.RFC3339) // 时间格式化
case []string:
return strings.Join(val, ",") // 数组序列化
default:
return fmt.Sprintf("%v", v)
}
}
// 🔗 使用自定义转换器
users = users.WithConverter(&CustomConverter{})
kv2doc 自动为所有字段建立索引,查询引擎智能选择最优路径:
// ✅ 索引加速查询(毫秒级响应)
users.Expr().WhereEq("email", "user@example.com") // 🔍 精确匹配
users.Expr().WhereLeftLike("name", "张") // 🔍 前缀搜索
users.Expr().WhereIn("status", []string{"active", "vip"}) // 🔍 多值匹配
// ⚠️ 全表扫描查询(相对较慢,但仍然高效)
users.Expr().WhereLike("name", "三") // 🔄 模糊匹配
users.Expr().WhereBetween("age", 25, 35) // 🔄 范围查询
// 💡 性能优化技巧:将高选择性条件置前
users.Expr().
WhereEq("email", email). // 高选择性优先
WhereBetween("age", 18, 65). // 低选择性后置
Find()
// 📊 查看查询执行计划
query := users.Expr().
WhereEq("is_active", true).
WhereBetween("age", 25, 35)
fmt.Printf("🔍 查询SQL: %s\n", query.ToSQL())
// 输出: FROM users WHERE (is_active == "true") AND (age BETWEEN 25 AND 35)
// 📈 性能监控
start := time.Now()
results, _ := query.Find()
duration := time.Since(start)
fmt.Printf("⏱️ 查询耗时: %v, 结果数量: %d\n", duration, len(results))
kv2doc 内置读写锁,天然支持高并发场景:
// 🎯 多协程安全并发读取
for i := 0; i < 10; i++ {
go func(id int) {
users, _ := userCollection.Find()
fmt.Printf("协程 %d: 读取到 %d 个用户\n", id, len(users))
}(i)
}
// ✍️ 写操作自动加锁保护
go func() {
userCollection.Insert(User{Name: "新用户"}) // 线程安全
}()
// 💡 最佳实践:读多写少的场景性能最佳
| 类型 | 描述 | 示例 |
|---|---|---|
DB | 数据库实例 | db, _ := kv2doc.NewDB("app.db") |
Collection[T] | 类型安全的集合操作 | users := NewCollection[User](db, "users") |
TypedDB[T] | 泛型数据库操作 | userDB := NewTypedDB[User](db) |
TypedDoc[T] | 泛型文档 | 包装用户数据的文档结构 |
Doc | 传统文档 (map[string]string) | doc := Doc{"name": "张三"} |
| 方法 | 功能描述 | 返回值 |
|---|---|---|
Insert(data T) | 插入文档 | (string, error) |
Update(id, data T) | 更新文档 | error |
Delete(id string) | 删除文档 | error |
FindByID(id string) | 根据ID查找 | (*TypedDoc[T], error) |
Find() | 查找所有文档 | ([]*TypedDoc[T], error) |
Count() | 统计文档数量 | (int64, error) |
| 方法 | 功能描述 | 返回值 |
|---|---|---|
Where(field, value) | 简单条件查询 | *TypedQuery[T] |
Query() | 创建类型安全查询构建器 | *TypedQuery[T] |
Expr() | 创建表达式风格查询构建器 | *EnhancedQuery[T] |
FindWhere(constraints...) | 约束条件查询 | ([]*TypedDoc[T], error) |
FindWithPaging(offset, size) | 分页查询 | ([]*TypedDoc[T], error) |
FindSorted(field, desc) | 排序查询 | ([]*TypedDoc[T], error) |
FindSortedWithPaging(field, desc, offset, size) | 排序+分页查询 | ([]*TypedDoc[T], error) |
| 方法 | 功能描述 | 返回值 |
|---|---|---|
Bulk() | 创建批量操作 | *TypedBulk[T] |
| 方法 | 功能描述 | 索引利用 |
|---|---|---|
Eq(field, value) | 等于查询 | ✅ 利用索引 |
Ne(field, value) | 不等于查询 | ❌ 全表扫描 |
Gt(field, value) | 大于查询 | ❌ 全表扫描 |
Gte(field, value) | 大于等于查询 | ❌ 全表扫描 |
Lt(field, value) | 小于查询 | ❌ 全表扫描 |
Lte(field, value) | 小于等于查询 | ❌ 全表扫描 |
Like(field, value) | 模糊匹配 | ❌ 全表扫描 |
LeftLike(field, value) | 前缀匹配 | ✅ 利用索引 |
RightLike(field, value) | 后缀匹配 | ❌ 全表扫描 |
| 方法 | 功能描述 | 返回值 |
|---|---|---|
OrderBy(field, desc) | 排序 | *TypedQuery[T] |
Limit(offset, size) | 分页 | *TypedQuery[T] |
| 方法 | 功能描述 | 返回值 |
|---|---|---|
First() | 获取第一个文档 | (*TypedDoc[T], error) |
Find() | 获取所有匹配文档 | ([]*TypedDoc[T], error) |
Count() | 统计匹配数量 | (int64, error) |
ForEach(fn) | 遍历匹配文档 | error |
| 函数 | 功能描述 | 示例 |
|---|---|---|
Equal[T](field, value) | 等值约束 | Equal[User]("age", 25) |
GreaterThan[T](field, value) | 大于约束 | GreaterThan[User]("age", 18) |
LessThan[T](field, value) | 小于约束 | LessThan[User]("age", 60) |
| 方法 | 功能描述 | 返回值 |
|---|---|---|
NewDB(path) | 创建/打开数据库 | (*DB, error) |
ByStore(store) | 使用自定义存储引擎 | *DB |
Add(table, doc) | 插入文档 | (string, error) |
Edit(table, id, doc) | 更新文档 | error |
Delete(table, id) | 删除文档 | error |
Query(table) | 创建查询 | *Query |
Bulk(table) | 创建批量操作 | *Bulk |
Drop(table) | 删除表 | error |
kv2doc 使用键值对存储,自动为每个字段建立索引:
假设有一个用户文档:
{
"_id": "123",
"name": "张三",
"age": "25",
"email": "zhangsan@example.com"
}
| 键类型 | 键 | 值 | 用途 |
|---|---|---|---|
| 主键 | p/_id/123 | {"_id":"123","name":"张三",...} | 存储完整文档 |
| 索引 | f/name/张三/123 | 123 | 姓名索引 |
| 索引 | f/age/25/123 | 123 | 年龄索引 |
| 索引 | f/email/zhangsan@example.com/123 | 123 | 邮箱索引 |
// 利用索引的查询
users.Query().Eq("email", "zhangsan@example.com") // 精确匹配
users.Query().LeftLike("name", "张") // 前缀匹配
// 需要全表扫描的查询
users.Query().Like("name", "三") // 中间匹配
users.Query().Gt("age", 18) // 范围查询
// ✅ 推荐:利用索引
users.Query().Eq("email", email) // 精确查找
users.Query().LeftLike("name", prefix) // 前缀搜索
// ❌ 避免:全表扫描
users.Query().Like("name", substring) // 中间匹配
users.Query().Gt("created_at", timestamp) // 范围查询
// ✅ 推荐:批量插入
bulk := users.Bulk()
for _, user := range manyUsers {
bulk.Insert(user)
}
bulk.Execute() // 一次事务完成
// ❌ 避免:逐个插入
for _, user := range manyUsers {
users.Insert(user) // 每次都是独立事务
}
// ✅ 推荐:数据库层分页
users.Query().Limit(offset, pageSize).Find()
// ❌ 避免:应用层分页
allUsers, _ := users.Find()
pagedUsers := allUsers[offset:offset+pageSize]
kv2doc 支持插件化存储引擎,只需实现 Store 接口:
type Store interface {
CreateTable(table string) error
DropTable(table string) error
SetKV(table string, kvs []KV) error
GetKV(table, key string) (KV, error)
ScanKV(table, prefix string, handle func(key string, value []byte) bool) error
NextID(table string) (string, error)
}
type MemoryStore struct {
data map[string]map[string][]byte
sequences map[string]uint64
mutex sync.RWMutex
}
func (m *MemoryStore) CreateTable(table string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
if m.data[table] == nil {
m.data[table] = make(map[string][]byte)
}
return nil
}
// 实现其他接口方法...
// 使用自定义存储
db := kv2doc.ByStore(&MemoryStore{
data: make(map[string]map[string][]byte),
sequences: make(map[string]uint64),
})
我们欢迎社区贡献!请查看 贡献指南 了解详情。
# 克隆仓库
git clone https://cnb.cool/zishuo/kv2doc.git
cd kv2doc
# 安装依赖
go mod tidy
# 运行测试
go test ./...
# 检查代码质量
go vet ./...
kv2doc 为 Go 开发者提供了从简单到优雅的三层API设计:
// 🔧 传统风格 - 向后兼容,快速迁移
db.Query("users").Eq("status", "active").List()
// 🛡️ 类型安全 - 编译时检查,IDE友好
users.Query().Eq("is_active", true).Find()
// 💎 表达式风格 - 最优雅,功能最强
users.Expr().WhereEq("is_active", true).WhereBetween("age", 18, 65).Find()
WhereEq, WhereBetween, WhereOr 语义清晰// 🆕 新项目推荐:表达式风格API
results, _ := users.Expr().
WhereEq("is_active", true). // 简洁优雅
Where("age", ">", 18). // 灵活操作符
WhereIn("role", []string{"user", "admin"}). // 丰富条件
OrderByDesc("created_at"). // 语义清晰
Page(1, 20). // 简单分页
Find()
// 💼 日常业务:类型安全API
users.Query().Eq("is_active", true).Find()
// 🔄 遗留系统:传统API
db.Query("users").Eq("is_active", "true").List()
本项目采用 MIT 许可证。详情请参阅 LICENSE 文件。
开始你的优雅查询之旅吧! 🚀