功能:增强 Schema 系统和添加新示例

- 扩展 Schema 支持更多数据类型(Duration、URL、JSON 等)
- 优化 SSTable 编码解码性能
- 添加多个新示例程序:
  - all_types: 展示所有支持的数据类型
  - new_types: 演示新增类型的使用
  - struct_tags: 展示结构体标签功能
  - time_duration: 时间和持续时间处理示例
- 完善测试用例和文档
- 优化代码结构和错误处理
This commit is contained in:
2025-10-10 00:20:45 +08:00
parent 6d04487789
commit 77087d36c6
41 changed files with 3008 additions and 452 deletions

View File

@@ -0,0 +1 @@
MANIFEST-000001

Binary file not shown.

View File

@@ -0,0 +1,34 @@
{
"version": 1,
"timestamp": 1760028726,
"checksum": "89e806ac5fbd5839456b425a2293097529b3edac6360f97afb06a1211d4fd53b",
"schema": {
"Name": "sensors",
"Fields": [
{
"Name": "device_id",
"Type": 9,
"Indexed": true,
"Comment": "设备ID"
},
{
"Name": "temperature",
"Type": 11,
"Indexed": false,
"Comment": "温度(摄氏度)"
},
{
"Name": "humidity",
"Type": 7,
"Indexed": false,
"Comment": "湿度0-100"
},
{
"Name": "online",
"Type": 14,
"Indexed": false,
"Comment": "是否在线"
}
]
}
}

View File

@@ -0,0 +1 @@
2

View File

@@ -0,0 +1,98 @@
package main
import (
"fmt"
"log"
"os"
"code.tczkiot.com/wlw/srdb"
)
func main() {
fmt.Println("=== SRDB 完整类型系统示例 ===\n")
// 清理旧数据
os.RemoveAll("./data")
// 示例 1: 展示所有类型
fmt.Println("=== 示例 1: 展示所有 14 种支持的类型 ===")
showAllTypes()
// 示例 2: 实际应用场景
fmt.Println("\n=== 示例 2: 实际应用 - 物联网传感器数据 ===")
sensorDataExample()
fmt.Println("\n✓ 所有示例执行成功!")
}
func showAllTypes() {
// 展示类型映射
types := []struct {
name string
goType string
srdbType srdb.FieldType
}{
{"有符号整数", "int", srdb.Int},
{"8位有符号整数", "int8", srdb.Int8},
{"16位有符号整数", "int16", srdb.Int16},
{"32位有符号整数", "int32", srdb.Int32},
{"64位有符号整数", "int64", srdb.Int64},
{"无符号整数", "uint", srdb.Uint},
{"8位无符号整数", "uint8 (byte)", srdb.Uint8},
{"16位无符号整数", "uint16", srdb.Uint16},
{"32位无符号整数", "uint32", srdb.Uint32},
{"64位无符号整数", "uint64", srdb.Uint64},
{"单精度浮点", "float32", srdb.Float32},
{"双精度浮点", "float64", srdb.Float64},
{"字符串", "string", srdb.String},
{"布尔", "bool", srdb.Bool},
}
fmt.Println("SRDB 类型系统(精确映射到 Go 基础类型):\n")
for i, t := range types {
fmt.Printf("%2d. %-20s %-20s -> %s\n", i+1, t.name, t.goType, t.srdbType.String())
}
}
func sensorDataExample() {
// 创建 Schema
schema, err := srdb.NewSchema("sensors", []srdb.Field{
{Name: "device_id", Type: srdb.Uint32, Indexed: true, Comment: "设备ID"},
{Name: "temperature", Type: srdb.Float32, Comment: "温度(摄氏度)"},
{Name: "humidity", Type: srdb.Uint8, Comment: "湿度0-100"},
{Name: "online", Type: srdb.Bool, Comment: "是否在线"},
})
if err != nil {
log.Fatal(err)
}
table, err := srdb.OpenTable(&srdb.TableOptions{
Dir: "./data/sensors",
Name: schema.Name,
Fields: schema.Fields,
})
if err != nil {
log.Fatal(err)
}
defer table.Close()
// 插入数据
sensors := []map[string]any{
{"device_id": uint32(1001), "temperature": float32(23.5), "humidity": uint8(65), "online": true},
{"device_id": uint32(1002), "temperature": float32(18.2), "humidity": uint8(72), "online": true},
{"device_id": uint32(1003), "temperature": float32(25.8), "humidity": uint8(58), "online": false},
}
err = table.Insert(sensors)
if err != nil {
log.Fatal(err)
}
fmt.Printf("✓ 插入 %d 个传感器数据\n", len(sensors))
fmt.Println("\n类型优势演示")
fmt.Println(" - device_id 使用 uint32 (节省空间,支持 42 亿设备)")
fmt.Println(" - temperature 使用 float32 (单精度足够,节省 50% 空间)")
fmt.Println(" - humidity 使用 uint8 (0-100 范围,仅需 1 字节)")
fmt.Println(" - online 使用 bool (语义清晰)")
}

View File

@@ -56,8 +56,8 @@ func example1() {
fmt.Println("=== 示例 1: 插入单个 map ===")
schema, err := srdb.NewSchema("users", []srdb.Field{
{Name: "name", Type: srdb.FieldTypeString, Comment: "用户名"},
{Name: "age", Type: srdb.FieldTypeInt64, Comment: "年龄"},
{Name: "name", Type: srdb.String, Comment: "用户名"},
{Name: "age", Type: srdb.Int64, Comment: "年龄"},
})
if err != nil {
log.Fatal(err)
@@ -93,9 +93,9 @@ func example2() {
fmt.Println("=== 示例 2: 批量插入 map 切片 ===")
schema, err := srdb.NewSchema("users", []srdb.Field{
{Name: "name", Type: srdb.FieldTypeString},
{Name: "age", Type: srdb.FieldTypeInt64},
{Name: "email", Type: srdb.FieldTypeString, Indexed: true},
{Name: "name", Type: srdb.String},
{Name: "age", Type: srdb.Int64},
{Name: "email", Type: srdb.String, Indexed: true},
})
if err != nil {
log.Fatal(err)
@@ -141,10 +141,10 @@ func example3() {
fmt.Println("=== 示例 3: 插入单个结构体 ===")
schema, err := srdb.NewSchema("users", []srdb.Field{
{Name: "name", Type: srdb.FieldTypeString},
{Name: "age", Type: srdb.FieldTypeInt64},
{Name: "email", Type: srdb.FieldTypeString},
{Name: "is_active", Type: srdb.FieldTypeBool},
{Name: "name", Type: srdb.String},
{Name: "age", Type: srdb.Int64},
{Name: "email", Type: srdb.String},
{Name: "is_active", Type: srdb.Bool},
})
if err != nil {
log.Fatal(err)
@@ -185,10 +185,10 @@ func example4() {
fmt.Println("=== 示例 4: 批量插入结构体切片 ===")
schema, err := srdb.NewSchema("users", []srdb.Field{
{Name: "name", Type: srdb.FieldTypeString},
{Name: "age", Type: srdb.FieldTypeInt64},
{Name: "email", Type: srdb.FieldTypeString},
{Name: "is_active", Type: srdb.FieldTypeBool},
{Name: "name", Type: srdb.String},
{Name: "age", Type: srdb.Int64},
{Name: "email", Type: srdb.String},
{Name: "is_active", Type: srdb.Bool},
})
if err != nil {
log.Fatal(err)
@@ -235,10 +235,10 @@ func example5() {
fmt.Println("=== 示例 5: 批量插入结构体指针切片 ===")
schema, err := srdb.NewSchema("users", []srdb.Field{
{Name: "name", Type: srdb.FieldTypeString},
{Name: "age", Type: srdb.FieldTypeInt64},
{Name: "email", Type: srdb.FieldTypeString},
{Name: "is_active", Type: srdb.FieldTypeBool},
{Name: "name", Type: srdb.String},
{Name: "age", Type: srdb.Int64},
{Name: "email", Type: srdb.String},
{Name: "is_active", Type: srdb.Bool},
})
if err != nil {
log.Fatal(err)
@@ -278,10 +278,10 @@ func example6() {
fmt.Println("=== 示例 6: 使用 snake_case 自动转换 ===")
schema, err := srdb.NewSchema("products", []srdb.Field{
{Name: "product_id", Type: srdb.FieldTypeString, Comment: "产品ID"},
{Name: "product_name", Type: srdb.FieldTypeString, Comment: "产品名称"},
{Name: "price", Type: srdb.FieldTypeFloat, Comment: "价格"},
{Name: "in_stock", Type: srdb.FieldTypeBool, Comment: "是否有货"},
{Name: "product_id", Type: srdb.String, Comment: "产品ID"},
{Name: "product_name", Type: srdb.String, Comment: "产品名称"},
{Name: "price", Type: srdb.Float64, Comment: "价格"},
{Name: "in_stock", Type: srdb.Bool, Comment: "是否有货"},
})
if err != nil {
log.Fatal(err)

View File

@@ -0,0 +1,129 @@
# SRDB 新类型系统示例
本示例展示 SRDB 最新的类型系统特性,包括新增的 **Byte**、**Rune**、**Decimal** 类型以及 **Nullable** 支持。
## 新增特性
### 1. Byte 类型 (FieldTypeByte)
- **用途**: 存储 0-255 范围的小整数
- **适用场景**: HTTP 状态码、标志位、小范围枚举值
- **优势**: 仅占 1 字节,相比 int64 节省 87.5% 空间
### 2. Rune 类型 (FieldTypeRune)
- **用途**: 存储单个 Unicode 字符
- **适用场景**: 等级标识S/A/B/C、单字符代码、Unicode 字符
- **优势**: 语义清晰,支持所有 Unicode 字符
### 3. Decimal 类型 (FieldTypeDecimal)
- **用途**: 高精度十进制数值
- **适用场景**: 金融计算、科学计算、需要精确数值的场景
- **优势**: 无精度损失,避免浮点数误差
- **实现**: 使用 `github.com/shopspring/decimal`
### 4. Nullable 支持
- **用途**: 允许字段值为 NULL
- **适用场景**: 可选字段、区分"未填写"和"空值"
- **使用**: 在 Field 定义中设置 `Nullable: true`
## 完整类型系统
SRDB 现在支持 **17 种**数据类型:
| 类别 | 类型 | 说明 |
|------|------|------|
| 有符号整数 | int, int8, int16, int32, int64 | 5 种 |
| 无符号整数 | uint, uint8, uint16, uint32, uint64 | 5 种 |
| 浮点 | float32, float64 | 2 种 |
| 字符串 | string | 1 种 |
| 布尔 | bool | 1 种 |
| 特殊类型 | byte, rune, decimal | 3 种 |
## 运行示例
```bash
cd examples/new_types
go run main.go
```
## 示例说明
### 示例 1: Byte 类型API 日志)
演示使用 `byte` 类型存储 HTTP 状态码,节省存储空间。
```go
{Name: "status_code", Type: srdb.FieldTypeByte, Comment: "HTTP 状态码"}
```
### 示例 2: Rune 类型(用户等级)
演示使用 `rune` 类型存储等级字符S/A/B/C/D
```go
{Name: "level", Type: srdb.FieldTypeRune, Comment: "等级字符"}
```
### 示例 3: Decimal 类型(金融交易)
演示使用 `decimal` 类型进行精确的金融计算。
```go
{Name: "amount", Type: srdb.FieldTypeDecimal, Comment: "交易金额(高精度)"}
// 使用示例
amount := decimal.NewFromFloat(1234.56789012345)
fee := decimal.NewFromFloat(1.23)
total := amount.Add(fee) // 精确加法,无误差
```
### 示例 4: Nullable 支持(用户资料)
演示可选字段的使用,允许某些字段为 NULL。
```go
{Name: "email", Type: srdb.FieldTypeString, Nullable: true, Comment: "邮箱(可选)"}
// 插入数据时可以为 nil
{"username": "Bob", "email": nil} // email 为 NULL
```
### 示例 5: 完整类型系统
演示所有 17 种类型在同一个表中的使用。
## 类型优势对比
| 场景 | 旧方案 | 新方案 | 优势 |
|------|--------|--------|------|
| HTTP 状态码 | int64 (8 字节) | byte (1 字节) | 节省 87.5% 空间 |
| 等级标识 | string ("S") | rune ('S') | 更精确的语义 |
| 金融金额 | float64 (有误差) | decimal (无误差) | 精确计算 |
| 可选字段 | 空字符串 "" | NULL | 区分未填写和空值 |
## 注意事项
1. **Byte 和 Rune 的底层类型**
- `byte` 在 Go 中等同于 `uint8`
- `rune` 在 Go 中等同于 `int32`
- 但在 SRDB Schema 中作为独立类型,语义更清晰
2. **Decimal 的使用**
- 需要导入 `github.com/shopspring/decimal`
- 创建方式:`decimal.NewFromFloat()`, `decimal.NewFromString()`, `decimal.NewFromInt()`
- 运算方法:`Add()`, `Sub()`, `Mul()`, `Div()`
3. **Nullable 的使用**
- NULL 值在 Go 中表示为 `nil`
- 读取时需要检查值是否存在且不为 nil
- 非 Nullable 字段不允许为 NULL会在验证时报错
## 最佳实践
1. **选择合适的类型**
- 使用最小的整数类型来节省空间(如 uint8 而非 int64
- 金融计算必须使用 decimal避免 float64
- 可选字段使用 Nullable而非空字符串或特殊值
2. **性能优化**
- 小整数类型byte, int8, uint16可减少存储和传输开销
- 索引字段选择合适的类型(如 byte 类型的索引比 string 更高效)
3. **数据完整性**
- 必填字段设置 `Nullable: false`
- 使用类型系统保证数据正确性
- Decimal 类型保证金融数据精确性

View File

@@ -0,0 +1 @@
MANIFEST-000001

Binary file not shown.

View File

@@ -0,0 +1,31 @@
{
"version": 1,
"timestamp": 1760030838,
"checksum": "db36b32950014d2d1551c398e67a7c463fd04bdf98be5a9dec64675cdb0882af",
"schema": {
"Name": "api_logs",
"Fields": [
{
"Name": "endpoint",
"Type": 13,
"Indexed": false,
"Nullable": false,
"Comment": "API 端点"
},
{
"Name": "status_code",
"Type": 15,
"Indexed": false,
"Nullable": false,
"Comment": "HTTP 状态码(用 byte 节省空间)"
},
{
"Name": "response_time_ms",
"Type": 8,
"Indexed": false,
"Nullable": false,
"Comment": "响应时间(毫秒)"
}
]
}
}

View File

@@ -0,0 +1 @@
2

15
examples/new_types/go.mod Normal file
View File

@@ -0,0 +1,15 @@
module code.tczkiot.com/wlw/srdb/examples/new_types
go 1.24.0
replace code.tczkiot.com/wlw/srdb => ../..
require (
code.tczkiot.com/wlw/srdb v0.0.0-00010101000000-000000000000
github.com/shopspring/decimal v1.4.0
)
require (
github.com/edsrzf/mmap-go v1.1.0 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
)

View File

@@ -0,0 +1,6 @@
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

378
examples/new_types/main.go Normal file
View File

@@ -0,0 +1,378 @@
package main
import (
"fmt"
"log"
"os"
"code.tczkiot.com/wlw/srdb"
"github.com/shopspring/decimal"
)
func main() {
fmt.Println("=== SRDB 新类型系统示例 ===")
// 清理旧数据
os.RemoveAll("./data")
// 示例 1: Byte 类型 - 适用于状态码、标志位等小整数
fmt.Println("\n=== 示例 1: Byte 类型(状态码)===")
byteExample()
// 示例 2: Rune 类型 - 适用于单个字符、等级标识等
fmt.Println("\n=== 示例 2: Rune 类型(等级字符)===")
runeExample()
// 示例 3: Decimal 类型 - 适用于金融计算、精确数值
fmt.Println("\n=== 示例 3: Decimal 类型(金融数据)===")
decimalExample()
// 示例 4: Nullable 支持 - 允许字段为 NULL
fmt.Println("\n=== 示例 4: Nullable 支持 ===")
nullableExample()
// 示例 5: 完整类型系统 - 展示所有 17 种类型
fmt.Println("\n=== 示例 5: 完整类型系统17 种类型)===")
allTypesExample()
fmt.Println("\n✓ 所有示例执行成功!")
}
// byteExample 演示 Byte 类型的使用
func byteExample() {
// 创建 Schema - 使用 byte 类型存储状态码
schema, err := srdb.NewSchema("api_logs", []srdb.Field{
{Name: "endpoint", Type: srdb.String, Comment: "API 端点"},
{Name: "status_code", Type: srdb.Byte, Comment: "HTTP 状态码(用 byte 节省空间)"},
{Name: "response_time_ms", Type: srdb.Uint16, Comment: "响应时间(毫秒)"},
})
if err != nil {
log.Fatal(err)
}
table, err := srdb.OpenTable(&srdb.TableOptions{
Dir: "./data/api_logs",
Name: schema.Name,
Fields: schema.Fields,
})
if err != nil {
log.Fatal(err)
}
defer table.Close()
// 插入数据 - status_code 使用 byte 类型0-255
logs := []map[string]any{
{"endpoint": "/api/users", "status_code": uint8(200), "response_time_ms": uint16(45)},
{"endpoint": "/api/orders", "status_code": uint8(201), "response_time_ms": uint16(89)},
{"endpoint": "/api/products", "status_code": uint8(255), "response_time_ms": uint16(12)},
{"endpoint": "/api/auth", "status_code": uint8(128), "response_time_ms": uint16(234)},
}
err = table.Insert(logs)
if err != nil {
log.Fatal(err)
}
fmt.Printf("✓ 插入 %d 条 API 日志\n", len(logs))
fmt.Println("类型优势:")
fmt.Println(" - status_code 使用 byte (仅 1 字节,相比 int64 节省 87.5% 空间)")
fmt.Println(" - response_time_ms 使用 uint16 (0-65535ms 范围足够)")
// 查询数据
row, _ := table.Get(1)
fmt.Printf("\n查询结果: endpoint=%s, status_code=%d, response_time=%dms\n",
row.Data["endpoint"], row.Data["status_code"], row.Data["response_time_ms"])
}
// runeExample 演示 Rune 类型的使用
func runeExample() {
// 创建 Schema - 使用 rune 类型存储等级字符
schema, err := srdb.NewSchema("user_levels", []srdb.Field{
{Name: "username", Type: srdb.String, Indexed: true, Comment: "用户名"},
{Name: "level", Type: srdb.Rune, Comment: "等级字符 (S/A/B/C/D)"},
{Name: "score", Type: srdb.Uint32, Comment: "积分"},
})
if err != nil {
log.Fatal(err)
}
table, err := srdb.OpenTable(&srdb.TableOptions{
Dir: "./data/user_levels",
Name: schema.Name,
Fields: schema.Fields,
})
if err != nil {
log.Fatal(err)
}
defer table.Close()
// 插入数据 - level 使用 rune 类型存储单个字符
users := []map[string]any{
{"username": "Alice", "level": rune('S'), "score": uint32(9500)},
{"username": "Bob", "level": rune('A'), "score": uint32(7200)},
{"username": "Charlie", "level": rune('B'), "score": uint32(5800)},
{"username": "David", "level": rune('C'), "score": uint32(3400)},
}
err = table.Insert(users)
if err != nil {
log.Fatal(err)
}
fmt.Printf("✓ 插入 %d 个用户等级数据\n", len(users))
fmt.Println("类型优势:")
fmt.Println(" - level 使用 rune 存储单个字符(语义清晰)")
fmt.Println(" - 支持 Unicode 字符,如中文等级:'甲'、'乙'、'丙'")
// 查询数据
row, _ := table.Get(1)
levelRune := row.Data["level"].(rune)
fmt.Printf("\n查询结果: username=%s, level=%c, score=%d\n",
row.Data["username"], levelRune, row.Data["score"])
}
// decimalExample 演示 Decimal 类型的使用
func decimalExample() {
// 创建 Schema - 使用 decimal 类型存储金融数据
schema, err := srdb.NewSchema("transactions", []srdb.Field{
{Name: "tx_id", Type: srdb.String, Indexed: true, Comment: "交易ID"},
{Name: "amount", Type: srdb.Decimal, Comment: "交易金额(高精度)"},
{Name: "fee", Type: srdb.Decimal, Comment: "手续费(高精度)"},
{Name: "currency", Type: srdb.String, Comment: "货币类型"},
})
if err != nil {
log.Fatal(err)
}
table, err := srdb.OpenTable(&srdb.TableOptions{
Dir: "./data/transactions",
Name: schema.Name,
Fields: schema.Fields,
})
if err != nil {
log.Fatal(err)
}
defer table.Close()
// 插入数据 - amount 和 fee 使用 decimal 类型(无精度损失)
transactions := []map[string]any{
{
"tx_id": "TX001",
"amount": decimal.NewFromFloat(1234.56789012345), // 高精度
"fee": decimal.NewFromFloat(1.23),
"currency": "USD",
},
{
"tx_id": "TX002",
"amount": decimal.RequireFromString("9876.543210987654321"), // 字符串创建,更精确
"fee": decimal.NewFromFloat(9.88),
"currency": "EUR",
},
{
"tx_id": "TX003",
"amount": decimal.NewFromFloat(0.00000001), // 极小值
"fee": decimal.NewFromFloat(0.0000001),
"currency": "BTC",
},
}
err = table.Insert(transactions)
if err != nil {
log.Fatal(err)
}
fmt.Printf("✓ 插入 %d 笔交易\n", len(transactions))
fmt.Println("类型优势:")
fmt.Println(" - decimal 类型无精度损失(使用 shopspring/decimal")
fmt.Println(" - 适合金融计算、科学计算等需要精确数值的场景")
fmt.Println(" - 避免浮点数运算误差(如 0.1 + 0.2 ≠ 0.3")
// 查询数据并进行计算
row, _ := table.Get(1)
amount := row.Data["amount"].(decimal.Decimal)
fee := row.Data["fee"].(decimal.Decimal)
total := amount.Add(fee) // decimal 类型的精确加法
fmt.Printf("\n查询结果: tx_id=%s, currency=%s\n", row.Data["tx_id"], row.Data["currency"])
fmt.Printf(" 金额: %s\n", amount.String())
fmt.Printf(" 手续费: %s\n", fee.String())
fmt.Printf(" 总计: %s (精确计算,无误差)\n", total.String())
}
// nullableExample 演示 Nullable 支持
func nullableExample() {
// 创建 Schema - 某些字段允许为 NULL
schema, err := srdb.NewSchema("user_profiles", []srdb.Field{
{Name: "username", Type: srdb.String, Nullable: false, Comment: "用户名(必填)"},
{Name: "email", Type: srdb.String, Nullable: true, Comment: "邮箱(可选)"},
{Name: "age", Type: srdb.Uint8, Nullable: true, Comment: "年龄(可选)"},
{Name: "bio", Type: srdb.String, Nullable: true, Comment: "个人简介(可选)"},
})
if err != nil {
log.Fatal(err)
}
table, err := srdb.OpenTable(&srdb.TableOptions{
Dir: "./data/user_profiles",
Name: schema.Name,
Fields: schema.Fields,
})
if err != nil {
log.Fatal(err)
}
defer table.Close()
// 插入数据 - 可选字段可以为 nil
profiles := []map[string]any{
{
"username": "Alice",
"email": "alice@example.com",
"age": uint8(25),
"bio": "Hello, I'm Alice!",
},
{
"username": "Bob",
"email": nil, // email 为 NULL
"age": uint8(30),
"bio": "Software Engineer",
},
{
"username": "Charlie",
"email": "charlie@example.com",
"age": nil, // age 为 NULL
"bio": nil, // bio 为 NULL
},
}
err = table.Insert(profiles)
if err != nil {
log.Fatal(err)
}
fmt.Printf("✓ 插入 %d 个用户资料(包含 NULL 值)\n", len(profiles))
fmt.Println("类型优势:")
fmt.Println(" - Nullable 字段可以为 NULL区分'未填写'和'空字符串'")
fmt.Println(" - 非 Nullable 字段必须有值,保证数据完整性")
// 查询数据
for i := 1; i <= 3; i++ {
row, _ := table.Get(int64(i))
fmt.Printf("\n用户 %d: username=%s", i, row.Data["username"])
if email, ok := row.Data["email"]; ok && email != nil {
fmt.Printf(", email=%s", email)
} else {
fmt.Print(", email=NULL")
}
if age, ok := row.Data["age"]; ok && age != nil {
fmt.Printf(", age=%d", age)
} else {
fmt.Print(", age=NULL")
}
}
fmt.Println()
}
// allTypesExample 展示所有 17 种类型
func allTypesExample() {
schema, err := srdb.NewSchema("all_types_demo", []srdb.Field{
// 有符号整数类型 (5 种)
{Name: "f_int", Type: srdb.Int, Comment: "int"},
{Name: "f_int8", Type: srdb.Int8, Comment: "int8"},
{Name: "f_int16", Type: srdb.Int16, Comment: "int16"},
{Name: "f_int32", Type: srdb.Int32, Comment: "int32"},
{Name: "f_int64", Type: srdb.Int64, Comment: "int64"},
// 无符号整数类型 (5 种)
{Name: "f_uint", Type: srdb.Uint, Comment: "uint"},
{Name: "f_uint8", Type: srdb.Uint8, Comment: "uint8"},
{Name: "f_uint16", Type: srdb.Uint16, Comment: "uint16"},
{Name: "f_uint32", Type: srdb.Uint32, Comment: "uint32"},
{Name: "f_uint64", Type: srdb.Uint64, Comment: "uint64"},
// 浮点类型 (2 种)
{Name: "f_float32", Type: srdb.Float32, Comment: "float32"},
{Name: "f_float64", Type: srdb.Float64, Comment: "float64"},
// 字符串类型 (1 种)
{Name: "f_string", Type: srdb.String, Comment: "string"},
// 布尔类型 (1 种)
{Name: "f_bool", Type: srdb.Bool, Comment: "bool"},
// 特殊类型 (3 种)
{Name: "f_byte", Type: srdb.Byte, Comment: "byte (=uint8)"},
{Name: "f_rune", Type: srdb.Rune, Comment: "rune (=int32)"},
{Name: "f_decimal", Type: srdb.Decimal, Comment: "decimal (高精度)"},
})
if err != nil {
log.Fatal(err)
}
table, err := srdb.OpenTable(&srdb.TableOptions{
Dir: "./data/all_types",
Name: schema.Name,
Fields: schema.Fields,
})
if err != nil {
log.Fatal(err)
}
defer table.Close()
// 插入包含所有类型的数据
record := map[string]any{
// 有符号整数
"f_int": int(-12345),
"f_int8": int8(-128),
"f_int16": int16(-32768),
"f_int32": int32(-2147483648),
"f_int64": int64(-9223372036854775808),
// 无符号整数
"f_uint": uint(12345),
"f_uint8": uint8(255),
"f_uint16": uint16(65535),
"f_uint32": uint32(4294967295),
"f_uint64": uint64(18446744073709551615),
// 浮点
"f_float32": float32(3.14159),
"f_float64": float64(2.718281828459045),
// 字符串
"f_string": "Hello, SRDB! 你好!",
// 布尔
"f_bool": true,
// 特殊类型
"f_byte": byte(255),
"f_rune": rune('中'),
"f_decimal": decimal.NewFromFloat(123456.789012345),
}
err = table.Insert(record)
if err != nil {
log.Fatal(err)
}
fmt.Println("✓ 插入包含所有 17 种类型的数据")
fmt.Println("\nSRDB 完整类型系统:")
fmt.Println(" 有符号整数: int, int8, int16, int32, int64 (5 种)")
fmt.Println(" 无符号整数: uint, uint8, uint16, uint32, uint64 (5 种)")
fmt.Println(" 浮点类型: float32, float64 (2 种)")
fmt.Println(" 字符串类型: string (1 种)")
fmt.Println(" 布尔类型: bool (1 种)")
fmt.Println(" 特殊类型: byte, rune, decimal (3 种)")
fmt.Println(" 总计: 17 种类型")
// 查询并验证数据
row, _ := table.Get(1)
fmt.Println("\n数据验证")
fmt.Printf(" f_int=%d, f_int64=%d\n", row.Data["f_int"], row.Data["f_int64"])
fmt.Printf(" f_uint=%d, f_uint64=%d\n", row.Data["f_uint"], row.Data["f_uint64"])
fmt.Printf(" f_float32=%f, f_float64=%f\n", row.Data["f_float32"], row.Data["f_float64"])
fmt.Printf(" f_string=%s\n", row.Data["f_string"])
fmt.Printf(" f_bool=%v\n", row.Data["f_bool"])
fmt.Printf(" f_byte=%d, f_rune=%c\n", row.Data["f_byte"], row.Data["f_rune"])
fmt.Printf(" f_decimal=%s\n", row.Data["f_decimal"].(decimal.Decimal).String())
}

View File

@@ -0,0 +1,132 @@
# Struct Tags 示例
本示例展示如何使用 Go struct tags 来定义 SRDB Schema包括完整的 nullable 支持。
## 功能特性
### Struct Tag 格式
SRDB 支持以下 struct tag 格式:
```go
type User struct {
// 基本格式
Name string `srdb:"name"`
// 指定索引
Email string `srdb:"email;indexed"`
// 标记为可空
Bio string `srdb:"bio;nullable"`
// 可空 + 索引
Phone string `srdb:"phone;nullable;indexed"`
// 完整格式
Age int64 `srdb:"age;indexed;nullable;comment:用户年龄"`
// 忽略字段
TempData string `srdb:"-"`
}
```
### Tag 说明
- **字段名**: 第一部分指定数据库字段名(可选,默认自动转换为 snake_case
- **indexed**: 标记该字段需要建立索引
- **nullable**: 标记该字段允许 NULL 值
- **comment**: 指定字段注释
- **-**: 忽略该字段(不包含在 Schema 中)
## 运行示例
```bash
cd examples/struct_tags
go run main.go
```
## 示例输出
```
=== SRDB Struct Tags Example ===
1. 从结构体生成 Schema
Schema 名称: users
字段数量: 8
字段详情:
- username: Type=string, Indexed=true, Nullable=false, Comment="用户名(索引)"
- age: Type=int64, Indexed=false, Nullable=false, Comment="年龄"
- email: Type=string, Indexed=false, Nullable=true, Comment="邮箱(可选)"
- phone_number: Type=string, Indexed=true, Nullable=true, Comment="手机号(可空且索引)"
- bio: Type=string, Indexed=false, Nullable=true, Comment="个人简介(可选)"
- avatar: Type=string, Indexed=false, Nullable=true, Comment="头像 URL可选"
- balance: Type=decimal, Indexed=false, Nullable=true, Comment="账户余额(可空)"
- is_active: Type=bool, Indexed=false, Nullable=false, Comment="是否激活"
2. 创建数据库和表
✓ 表创建成功
3. 插入完整数据
✓ 插入用户 alice完整数据
4. 插入部分数据(可选字段为 NULL
✓ 插入用户 bobemail、bio、balance 为 NULL
5. 测试必填字段不能为 NULL
✓ 符合预期的错误: field username: NULL value not allowed (field is not nullable)
6. 查询所有用户
用户: alice, 邮箱: alice@example.com, 余额: 1000.5
用户: bob, 邮箱: <NULL>, 余额: <NULL>
7. 按索引字段查询username='alice'
找到用户: alice, 年龄: 25
✅ 所有操作完成!
```
## 自动字段名转换
如果不指定字段名,会自动将结构体字段名转换为 snake_case
```go
type User struct {
UserName string // -> user_name
EmailAddress string // -> email_address
IsActive bool // -> is_active
HTTPServer string // -> http_server
}
```
## Nullable 支持说明
1. **必填字段**(默认):不能插入 NULL 值,会返回错误
2. **可选字段**nullable=true可以插入 NULL 值
3. **查询结果**NULL 值会以 `nil` 形式返回
4. **验证时机**:在 `Insert()` 时自动验证
## 最佳实践
1. **对可选字段使用 nullable**
```go
Email string `srdb:"email;nullable"` // ✓ 推荐
Email string `srdb:"email"` // ✗ 如果是可选的,应该标记 nullable
```
2. **对查询频繁的字段建立索引**
```go
Username string `srdb:"username;indexed"` // ✓ 查询键
Bio string `srdb:"bio"` // ✓ 不常查询的字段
```
3. **组合使用 nullable 和 indexed**
```go
Phone string `srdb:"phone;nullable;indexed"` // ✓ 可选但需要索引查询
```
4. **为可选字段标记 nullable**
```go
Avatar string `srdb:"avatar;nullable"` // ✓ 值类型 + nullable
Balance decimal.Decimal `srdb:"balance;nullable"` // ✓ 所有类型都支持 nullable
```

View File

@@ -0,0 +1,10 @@
{
"version": 1,
"tables": [
{
"name": "users",
"dir": "users",
"created_at": 1760032030
}
]
}

View File

@@ -0,0 +1 @@
MANIFEST-000001

Binary file not shown.

View File

@@ -0,0 +1,66 @@
{
"version": 1,
"timestamp": 1760032030,
"checksum": "1fdebae19ecf3cfcecddfa11ddcfc9c4b6656e1fd2477df8849639c108f4ada1",
"schema": {
"Name": "users",
"Fields": [
{
"Name": "username",
"Type": 13,
"Indexed": true,
"Nullable": false,
"Comment": "用户名(索引)"
},
{
"Name": "age",
"Type": 5,
"Indexed": false,
"Nullable": false,
"Comment": "年龄"
},
{
"Name": "email",
"Type": 13,
"Indexed": false,
"Nullable": true,
"Comment": "邮箱(可选)"
},
{
"Name": "phone_number",
"Type": 13,
"Indexed": true,
"Nullable": true,
"Comment": "手机号(可空且索引)"
},
{
"Name": "bio",
"Type": 13,
"Indexed": false,
"Nullable": true,
"Comment": "个人简介(可选)"
},
{
"Name": "avatar",
"Type": 13,
"Indexed": false,
"Nullable": true,
"Comment": "头像 URL可选"
},
{
"Name": "balance",
"Type": 17,
"Indexed": false,
"Nullable": true,
"Comment": "账户余额(可空)"
},
{
"Name": "is_active",
"Type": 14,
"Indexed": false,
"Nullable": false,
"Comment": "是否激活"
}
]
}
}

View File

@@ -0,0 +1 @@
2

View File

@@ -0,0 +1,15 @@
module code.tczkiot.com/wlw/srdb/examples/struct_tags
go 1.24.0
replace code.tczkiot.com/wlw/srdb => ../..
require (
code.tczkiot.com/wlw/srdb v0.0.0-00010101000000-000000000000
github.com/shopspring/decimal v1.4.0
)
require (
github.com/edsrzf/mmap-go v1.1.0 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
)

View File

@@ -0,0 +1,6 @@
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -0,0 +1,176 @@
package main
import (
"fmt"
"log"
"code.tczkiot.com/wlw/srdb"
"github.com/shopspring/decimal"
)
// User 用户结构体,展示 struct tag 的完整使用
type User struct {
// 基本字段(必填)
Username string `srdb:"username;indexed;comment:用户名(索引)"`
Age int64 `srdb:"age;comment:年龄"`
// 可选字段nullable
Email string `srdb:"email;nullable;comment:邮箱(可选)"`
PhoneNumber string `srdb:"phone_number;nullable;indexed;comment:手机号(可空且索引)"`
Bio string `srdb:"bio;nullable;comment:个人简介(可选)"`
Avatar string `srdb:"avatar;nullable;comment:头像 URL可选"`
// 财务字段
Balance decimal.Decimal `srdb:"balance;nullable;comment:账户余额(可空)"`
// 布尔字段
IsActive bool `srdb:"is_active;comment:是否激活"`
// 忽略字段
internalData string `srdb:"-"` // 未导出字段会自动忽略
TempData string `srdb:"-"` // 使用 "-" 显式忽略导出字段
}
func main() {
fmt.Println("=== SRDB Struct Tags Example ===\n")
// 1. 从结构体生成 Schema
fmt.Println("1. 从结构体生成 Schema")
fields, err := srdb.StructToFields(User{})
if err != nil {
log.Fatalf("StructToFields failed: %v", err)
}
schema, err := srdb.NewSchema("users", fields)
if err != nil {
log.Fatalf("NewSchema failed: %v", err)
}
fmt.Printf("Schema 名称: %s\n", schema.Name)
fmt.Printf("字段数量: %d\n\n", len(schema.Fields))
// 打印所有字段
fmt.Println("字段详情:")
for _, field := range schema.Fields {
fmt.Printf(" - %s: Type=%s, Indexed=%v, Nullable=%v",
field.Name, field.Type, field.Indexed, field.Nullable)
if field.Comment != "" {
fmt.Printf(", Comment=%q", field.Comment)
}
fmt.Println()
}
fmt.Println()
// 2. 创建数据库和表
fmt.Println("2. 创建数据库和表")
db, err := srdb.Open("./data")
if err != nil {
log.Fatalf("Open database failed: %v", err)
}
defer db.Close()
table, err := db.CreateTable("users", schema)
if err != nil {
log.Fatalf("CreateTable failed: %v", err)
}
fmt.Println("✓ 表创建成功\n")
// 3. 插入数据 - 完整数据(所有字段都有值)
fmt.Println("3. 插入完整数据")
avatar1 := "https://example.com/avatar1.png"
err = table.Insert(map[string]any{
"username": "alice",
"age": int64(25),
"email": "alice@example.com",
"phone_number": "13800138001",
"bio": "Software Engineer",
"avatar": avatar1,
"balance": decimal.NewFromFloat(1000.50),
"is_active": true,
})
if err != nil {
log.Fatalf("Insert failed: %v", err)
}
fmt.Println("✓ 插入用户 alice完整数据")
// 4. 插入数据 - 部分字段为 NULL
fmt.Println("\n4. 插入部分数据(可选字段为 NULL")
err = table.Insert(map[string]any{
"username": "bob",
"age": int64(30),
"email": nil, // NULL 值
"bio": nil, // NULL 值
"balance": nil, // NULL 值
"is_active": true,
})
if err != nil {
log.Fatalf("Insert failed: %v", err)
}
fmt.Println("✓ 插入用户 bobemail、bio、balance 为 NULL")
// 5. 插入数据 - 必填字段不能为 NULL
fmt.Println("\n5. 测试必填字段不能为 NULL")
err = table.Insert(map[string]any{
"username": nil, // 尝试将必填字段设为 NULL
"age": int64(28),
"is_active": true,
})
if err != nil {
fmt.Printf("✓ 符合预期的错误: %v\n", err)
} else {
log.Fatal("应该返回错误,但成功了!")
}
// 6. 查询所有数据
fmt.Println("\n6. 查询所有用户")
rows, err := table.Query().Rows()
if err != nil {
log.Fatalf("Query failed: %v", err)
}
defer rows.Close()
for rows.Next() {
row := rows.Row()
data := row.Data()
username := data["username"]
email := data["email"]
balance := data["balance"]
fmt.Printf(" 用户: %v", username)
if email == nil {
fmt.Printf(", 邮箱: <NULL>")
} else {
fmt.Printf(", 邮箱: %v", email)
}
if balance == nil {
fmt.Printf(", 余额: <NULL>")
} else {
fmt.Printf(", 余额: %v", balance)
}
fmt.Println()
}
// 7. 按索引字段查询
fmt.Println("\n7. 按索引字段查询username='alice'")
rows2, err := table.Query().Eq("username", "alice").Rows()
if err != nil {
log.Fatalf("Query failed: %v", err)
}
defer rows2.Close()
if rows2.Next() {
row := rows2.Row()
data := row.Data()
fmt.Printf(" 找到用户: %v, 年龄: %v\n", data["username"], data["age"])
}
fmt.Println("\n✅ 所有操作完成!")
fmt.Println("\nStruct Tag 使用总结:")
fmt.Println(" - srdb:\"name\" # 指定字段名")
fmt.Println(" - srdb:\"name;indexed\" # 字段名 + 索引")
fmt.Println(" - srdb:\"name;nullable\" # 字段名 + 可空")
fmt.Println(" - srdb:\"name;comment:注释\" # 字段名 + 注释")
fmt.Println(" - srdb:\"name;indexed;nullable;comment:XX\" # 完整格式")
fmt.Println(" - srdb:\"-\" # 忽略字段")
}

View File

@@ -0,0 +1,140 @@
package main
import (
"fmt"
"log"
"os"
"time"
"code.tczkiot.com/wlw/srdb"
)
func main() {
fmt.Println("=== Testing Time and Duration Types ===\n")
// 1. 创建 Schema
schema, err := srdb.NewSchema("events", []srdb.Field{
{Name: "name", Type: srdb.String, Comment: "事件名称"},
{Name: "created_at", Type: srdb.Time, Comment: "创建时间"},
{Name: "duration", Type: srdb.Duration, Comment: "持续时间"},
{Name: "count", Type: srdb.Int64, Comment: "计数"},
})
if err != nil {
log.Fatalf("创建 Schema 失败: %v", err)
}
fmt.Println("✓ Schema 创建成功")
fmt.Printf(" 字段数: %d\n", len(schema.Fields))
for _, field := range schema.Fields {
fmt.Printf(" - %s: %s (%s)\n", field.Name, field.Type.String(), field.Comment)
}
fmt.Println()
// 2. 创建数据库和表
os.RemoveAll("./test_time_data")
db, err := srdb.Open("./test_time_data")
if err != nil {
log.Fatalf("打开数据库失败: %v", err)
}
defer func() {
db.Close()
os.RemoveAll("./test_time_data")
}()
table, err := db.CreateTable("events", schema)
if err != nil {
log.Fatalf("创建表失败: %v", err)
}
fmt.Println("✓ 表创建成功\n")
// 3. 插入数据(使用原生类型)
now := time.Now()
duration := 2 * time.Hour
err = table.Insert(map[string]any{
"name": "event1",
"created_at": now,
"duration": duration,
"count": int64(100),
})
if err != nil {
log.Fatalf("插入数据失败: %v", err)
}
fmt.Println("✓ 插入数据成功(使用原生类型)")
fmt.Printf(" 时间: %v\n", now)
fmt.Printf(" 持续时间: %v\n", duration)
fmt.Println()
// 4. 插入数据(使用字符串格式)
err = table.Insert(map[string]any{
"name": "event2",
"created_at": now.Format(time.RFC3339),
"duration": "1h30m",
"count": int64(200),
})
if err != nil {
log.Fatalf("插入数据失败(字符串格式): %v", err)
}
fmt.Println("✓ 插入数据成功(使用字符串格式)")
fmt.Printf(" 时间字符串: %s\n", now.Format(time.RFC3339))
fmt.Printf(" 持续时间字符串: 1h30m\n")
fmt.Println()
// 5. 插入数据(使用 int64 格式)
err = table.Insert(map[string]any{
"name": "event3",
"created_at": now.Unix(),
"duration": int64(45 * time.Minute),
"count": int64(300),
})
if err != nil {
log.Fatalf("插入数据失败int64 格式): %v", err)
}
fmt.Println("✓ 插入数据成功(使用 int64 格式)")
fmt.Printf(" Unix 时间戳: %d\n", now.Unix())
fmt.Printf(" 持续时间(纳秒): %d\n", int64(45*time.Minute))
fmt.Println()
// 6. 查询数据
fmt.Println("6. 查询所有数据")
rows, err := table.Query().Rows()
if err != nil {
log.Fatalf("查询失败: %v", err)
}
defer rows.Close()
count := 0
for rows.Next() {
count++
row := rows.Row()
data := row.Data()
name := data["name"]
createdAt := data["created_at"]
dur := data["duration"]
cnt := data["count"]
fmt.Printf(" [%d] 名称: %v\n", count, name)
// 验证类型
if t, ok := createdAt.(time.Time); ok {
fmt.Printf(" 时间: %v (类型: time.Time) ✓\n", t.Format(time.RFC3339))
} else {
fmt.Printf(" 时间: %v (类型: %T) ✗\n", createdAt, createdAt)
}
if d, ok := dur.(time.Duration); ok {
fmt.Printf(" 持续时间: %v (类型: time.Duration) ✓\n", d)
} else {
fmt.Printf(" 持续时间: %v (类型: %T) ✗\n", dur, dur)
}
fmt.Printf(" 计数: %v\n\n", cnt)
}
fmt.Printf("✅ 所有测试完成! 共查询 %d 条记录\n", count)
}

View File

@@ -0,0 +1,106 @@
package main
import (
"fmt"
"log"
"os"
"time"
"code.tczkiot.com/wlw/srdb"
)
func main() {
fmt.Println("=== Testing Time and Duration Types (Simple) ===\n")
// 1. 创建 Schema
schema, err := srdb.NewSchema("events", []srdb.Field{
{Name: "name", Type: srdb.String, Comment: "事件名称"},
{Name: "created_at", Type: srdb.Time, Comment: "创建时间"},
{Name: "duration", Type: srdb.Duration, Comment: "持续时间"},
})
if err != nil {
log.Fatalf("创建 Schema 失败: %v", err)
}
fmt.Println("✓ Schema 创建成功")
for _, field := range schema.Fields {
fmt.Printf(" - %s: %s\n", field.Name, field.Type.String())
}
fmt.Println()
// 2. 创建数据库和表
os.RemoveAll("./test_data")
db, err := srdb.Open("./test_data")
if err != nil {
log.Fatalf("打开数据库失败: %v", err)
}
table, err := db.CreateTable("events", schema)
if err != nil {
log.Fatalf("创建表失败: %v", err)
}
fmt.Println("✓ 表创建成功\n")
// 3. 插入数据
now := time.Now()
duration := 2 * time.Hour
err = table.Insert(map[string]any{
"name": "event1",
"created_at": now,
"duration": duration,
})
if err != nil {
log.Fatalf("插入数据失败: %v", err)
}
fmt.Println("✓ 插入数据成功")
fmt.Printf(" 时间: %v\n", now.Format(time.RFC3339))
fmt.Printf(" 持续时间: %v\n\n", duration)
// 4. 立即查询(从 MemTable
fmt.Println("4. 查询数据(从 MemTable")
rows, err := table.Query().Rows()
if err != nil {
log.Fatalf("查询失败: %v", err)
}
defer rows.Close()
success := true
for rows.Next() {
row := rows.Row()
data := row.Data()
name := data["name"]
createdAt := data["created_at"]
dur := data["duration"]
fmt.Printf(" 名称: %v\n", name)
// 验证类型
if t, ok := createdAt.(time.Time); ok {
fmt.Printf(" 时间: %v (类型: time.Time) ✓\n", t.Format(time.RFC3339))
} else {
fmt.Printf(" 时间: %v (类型: %T) ✗ FAILED\n", createdAt, createdAt)
success = false
}
if d, ok := dur.(time.Duration); ok {
fmt.Printf(" 持续时间: %v (类型: time.Duration) ✓\n", d)
} else {
fmt.Printf(" 持续时间: %v (类型: %T) ✗ FAILED\n", dur, dur)
success = false
}
}
if success {
fmt.Println("\n✅ 测试通过! Time 和 Duration 类型正确保留")
} else {
fmt.Println("\n❌ 测试失败! 类型未正确保留")
os.Exit(1)
}
// 快速退出,不等待清理
os.Exit(0)
}

View File

@@ -24,20 +24,20 @@ func StartWebUI(dbPath string, addr string) {
// 创建示例 Schema
userSchema, err := srdb.NewSchema("users", []srdb.Field{
{Name: "name", Type: srdb.FieldTypeString, Indexed: true, Comment: "User name"},
{Name: "email", Type: srdb.FieldTypeString, Indexed: false, Comment: "Email address"},
{Name: "age", Type: srdb.FieldTypeInt64, Indexed: false, Comment: "Age"},
{Name: "city", Type: srdb.FieldTypeString, Indexed: false, Comment: "City"},
{Name: "name", Type: srdb.String, Indexed: true, Comment: "User name"},
{Name: "email", Type: srdb.String, Indexed: false, Comment: "Email address"},
{Name: "age", Type: srdb.Int64, Indexed: false, Comment: "Age"},
{Name: "city", Type: srdb.String, Indexed: false, Comment: "City"},
})
if err != nil {
log.Fatal(err)
}
productSchema, err := srdb.NewSchema("products", []srdb.Field{
{Name: "product_name", Type: srdb.FieldTypeString, Indexed: true, Comment: "Product name"},
{Name: "price", Type: srdb.FieldTypeFloat, Indexed: false, Comment: "Price"},
{Name: "quantity", Type: srdb.FieldTypeInt64, Indexed: false, Comment: "Quantity"},
{Name: "category", Type: srdb.FieldTypeString, Indexed: false, Comment: "Category"},
{Name: "product_name", Type: srdb.String, Indexed: true, Comment: "Product name"},
{Name: "price", Type: srdb.Float64, Indexed: false, Comment: "Price"},
{Name: "quantity", Type: srdb.Int64, Indexed: false, Comment: "Quantity"},
{Name: "category", Type: srdb.String, Indexed: false, Comment: "Category"},
})
if err != nil {
log.Fatal(err)
@@ -140,10 +140,10 @@ func autoInsertData(db *srdb.Database) {
if !hasLogs {
logsSchema, err := srdb.NewSchema("logs", []srdb.Field{
{Name: "group", Type: srdb.FieldTypeString, Indexed: true, Comment: "Log group (A-E)"},
{Name: "timestamp", Type: srdb.FieldTypeString, Indexed: false, Comment: "Timestamp"},
{Name: "data", Type: srdb.FieldTypeString, Indexed: false, Comment: "Random data"},
{Name: "size_bytes", Type: srdb.FieldTypeInt64, Indexed: false, Comment: "Data size in bytes"},
{Name: "group", Type: srdb.String, Indexed: true, Comment: "Log group (A-E)"},
{Name: "timestamp", Type: srdb.String, Indexed: false, Comment: "Timestamp"},
{Name: "data", Type: srdb.String, Indexed: false, Comment: "Random data"},
{Name: "size_bytes", Type: srdb.Int64, Indexed: false, Comment: "Data size in bytes"},
})
if err != nil {
log.Fatal(err)