Files
elowdb-go/examples/custom-json/main.go

538 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"fmt"
"log"
"os"
"sort"
"strconv"
"strings"
"time"
"linedb/pkg/linedb"
)
// User представляет пользователя
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
IsActive bool `json:"isActive"`
Role string `json:"role"`
CreatedAt int64 `json:"createdAt"`
}
// Product представляет продукт
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
Category string `json:"category"`
InStock bool `json:"inStock"`
SellerID int `json:"sellerId"`
CreatedAt int64 `json:"createdAt"`
}
func main() {
// Очищаем тестовые данные
os.RemoveAll("./data/test-linedb-custom-json")
fmt.Println("=== LineDb Custom JSON Serialization Demo ===")
// Получаем номера тестов из аргументов командной строки
testNumbers := parseTestNumbers()
// Выполняем тесты
if len(testNumbers) == 0 {
// Если номера не указаны, выполняем все тесты
runAllTests()
} else {
// Выполняем только указанные тесты
runSelectedTests(testNumbers)
}
fmt.Println("\n=== Все тесты завершены ===")
}
// parseTestNumbers парсит номера тестов из аргументов командной строки
func parseTestNumbers() []int {
if len(os.Args) < 2 {
return []int{}
}
// Получаем аргумент после имени программы
arg := os.Args[1]
// Проверяем, что это не флаг помощи
if arg == "-h" || arg == "--help" || arg == "help" {
printUsage()
return []int{}
}
// Разбиваем по запятой
parts := strings.Split(arg, ",")
var numbers []int
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
num, err := strconv.Atoi(part)
if err != nil {
fmt.Printf("Ошибка: '%s' не является числом\n", part)
printUsage()
return []int{}
}
// Проверяем диапазон
if num < 1 || num > 4 {
fmt.Printf("Ошибка: номер теста %d должен быть от 1 до 4\n", num)
printUsage()
return []int{}
}
numbers = append(numbers, num)
}
return numbers
}
// printUsage выводит справку по использованию
func printUsage() {
fmt.Println("\nИспользование:")
fmt.Println(" go run main.go # Запустить все тесты")
fmt.Println(" go run main.go 1 # Запустить только тест 1")
fmt.Println(" go run main.go 1,3 # Запустить тесты 1 и 3")
fmt.Println(" go run main.go 2,3,4 # Запустить тесты 2, 3 и 4")
fmt.Println(" go run main.go help # Показать эту справку")
fmt.Println("\nДоступные тесты:")
fmt.Println(" 1 - Использование go-json (по умолчанию)")
fmt.Println(" 2 - Использование стандартного encoding/json")
fmt.Println(" 3 - Использование кастомной функции сериализации")
fmt.Println(" 4 - ID всегда первое поле")
os.Exit(0)
}
// runAllTests запускает все тесты
func runAllTests() {
testDefaultJSON()
testStandardJSON()
testCustomJSON()
testIDFirstField()
}
// runSelectedTests запускает только выбранные тесты
func runSelectedTests(numbers []int) {
for _, num := range numbers {
switch num {
case 1:
fmt.Println("1. Использование go-json (по умолчанию)")
testDefaultJSON()
case 2:
fmt.Println("2. Использование стандартного encoding/json")
testStandardJSON()
case 3:
fmt.Println("3. Использование кастомной функции сериализации")
testCustomJSON()
case 4:
fmt.Println("4. ID всегда первое поле")
testIDFirstField()
}
}
}
func testDefaultJSON() {
// Создаем опции инициализации с go-json (по умолчанию)
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-custom-json/default",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
},
},
}
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем пользователя
user := map[string]any{
"id": 1,
"username": "default_user",
"email": "default@example.com",
"isActive": true,
"role": "user",
"createdAt": time.Now().Unix(),
}
// Вставляем данные
if err := db.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert user: %v", err)
return
}
// Читаем данные
result, err := db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
fmt.Printf(" Пользователей в базе: %d\n", len(result))
if len(result) > 0 {
if record, ok := result[0].(map[string]any); ok {
fmt.Printf(" ID: %v, Username: %s\n", record["id"], record["username"])
}
}
}
func testStandardJSON() {
// Создаем функции сериализации с использованием стандартного encoding/json
standardJSONMarshal := func(v any) ([]byte, error) {
return json.Marshal(v)
}
standardJSONUnmarshal := func(data []byte, v any) error {
return json.Unmarshal(data, v)
}
// Создаем опции инициализации со стандартным JSON
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-custom-json/standard",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
JSONMarshal: standardJSONMarshal,
JSONUnmarshal: standardJSONUnmarshal,
},
},
}
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем пользователя
user := map[string]any{
"id": 2,
"username": "standard_user",
"email": "standard@example.com",
"isActive": true,
"role": "admin",
"createdAt": time.Now().Unix(),
}
// Вставляем данные
if err := db.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert user: %v", err)
return
}
// Читаем данные
result, err := db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
fmt.Printf(" Пользователей в базе: %d\n", len(result))
if len(result) > 0 {
if record, ok := result[0].(map[string]any); ok {
fmt.Printf(" ID: %v, Username: %s\n", record["id"], record["username"])
}
}
}
func testCustomJSON() {
// Создаем кастомные функции сериализации с дополнительной логикой
customJSONMarshal := func(v any) ([]byte, error) {
// Добавляем метаданные к сериализации
data := map[string]any{
"data": v,
"timestamp": time.Now().Unix(),
"version": "1.0",
}
return json.Marshal(data)
}
customJSONUnmarshal := func(data []byte, v any) error {
// Извлекаем данные из кастомного формата
var wrapper map[string]any
if err := json.Unmarshal(data, &wrapper); err != nil {
return err
}
// Извлекаем основную часть данных
if dataField, exists := wrapper["data"]; exists {
// Сериализуем обратно в JSON и десериализуем в целевой тип
dataBytes, err := json.Marshal(dataField)
if err != nil {
return err
}
return json.Unmarshal(dataBytes, v)
}
// Если нет поля data, пытаемся десериализовать как обычный JSON
return json.Unmarshal(data, v)
}
// Создаем опции инициализации с кастомными функциями
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-custom-json/custom",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
JSONMarshal: customJSONMarshal,
JSONUnmarshal: customJSONUnmarshal,
},
{
CollectionName: "products",
AllocSize: 256,
IndexedFields: []string{"id", "name"},
JSONMarshal: customJSONMarshal,
JSONUnmarshal: customJSONUnmarshal,
},
},
}
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем пользователя
user := map[string]any{
"id": 3,
"username": "custom_user",
"email": "custom@example.com",
"isActive": true,
"role": "moderator",
"createdAt": time.Now().Unix(),
}
// Создаем продукт
product := map[string]any{
"id": 1,
"name": "Custom Product",
"price": 99.99,
"category": "Electronics",
"inStock": true,
"sellerId": 1,
"createdAt": time.Now().Unix(),
}
// Вставляем данные
if err := db.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert user: %v", err)
return
}
if err := db.Insert(product, "products", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert product: %v", err)
return
}
// Читаем данные
users, err := db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
products, err := db.Read("products", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read products: %v", err)
return
}
fmt.Printf(" Пользователей в базе: %d\n", len(users))
fmt.Printf(" Продуктов в базе: %d\n", len(products))
if len(users) > 0 {
if record, ok := users[0].(map[string]any); ok {
fmt.Printf(" Пользователь ID: %v, Username: %s\n", record["id"], record["username"])
}
}
if len(products) > 0 {
if record, ok := products[0].(map[string]any); ok {
fmt.Printf(" Продукт ID: %v, Name: %s, Price: %v\n", record["id"], record["name"], record["price"])
}
}
}
func testIDFirstField() {
// Создаем функции сериализации где id всегда первое поле
idFirstJSONMarshal := func(v any) ([]byte, error) {
if data, ok := v.(map[string]any); ok {
var parts []string
// Сначала добавляем id если он есть
if id, exists := data["id"]; exists {
idBytes, err := json.Marshal(id)
if err != nil {
return nil, err
}
parts = append(parts, fmt.Sprintf(`"id":%s`, string(idBytes)))
}
// Затем добавляем все остальные поля в алфавитном порядке
var keys []string
for key := range data {
if key != "id" {
keys = append(keys, key)
}
}
sort.Strings(keys)
for _, key := range keys {
valueBytes, err := json.Marshal(data[key])
if err != nil {
return nil, err
}
parts = append(parts, fmt.Sprintf(`"%s":%s`, key, string(valueBytes)))
}
jsonStr := "{" + strings.Join(parts, ",") + "}"
return []byte(jsonStr), nil
}
// Если это не map, используем стандартную сериализацию
return json.Marshal(v)
}
idFirstJSONUnmarshal := func(data []byte, v any) error {
return json.Unmarshal(data, v)
}
// Создаем опции инициализации с функциями id-first
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-custom-json/id-first",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
JSONMarshal: idFirstJSONMarshal,
JSONUnmarshal: idFirstJSONUnmarshal,
},
{
CollectionName: "products",
AllocSize: 256,
IndexedFields: []string{"id", "name"},
JSONMarshal: idFirstJSONMarshal,
JSONUnmarshal: idFirstJSONUnmarshal,
},
},
}
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем пользователя (id не первое поле в исходных данных)
user := map[string]any{
"username": "id_first_user",
"email": "id_first@example.com",
"id": 1,
"isActive": true,
"role": "user",
"createdAt": time.Now().Unix(),
}
// Создаем продукт (id не первое поле в исходных данных)
product := map[string]any{
"name": "ID First Product",
"price": 123.45,
"id": 2,
"category": "Books",
"inStock": false,
"sellerId": 1,
"createdAt": time.Now().Unix(),
}
// Вставляем данные
if err := db.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert user: %v", err)
return
}
if err := db.Insert(product, "products", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert product: %v", err)
return
}
// Читаем данные
users, err := db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
products, err := db.Read("products", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read products: %v", err)
return
}
fmt.Printf(" Пользователей в базе: %d\n", len(users))
fmt.Printf(" Продуктов в базе: %d\n", len(products))
if len(users) > 0 {
if record, ok := users[0].(map[string]any); ok {
fmt.Printf(" Пользователь ID: %v, Username: %s\n", record["id"], record["username"])
}
}
if len(products) > 0 {
if record, ok := products[0].(map[string]any); ok {
fmt.Printf(" Продукт ID: %v, Name: %s, Price: %v\n", record["id"], record["name"], record["price"])
}
}
// Показываем содержимое файла для демонстрации
fmt.Println(" Проверьте файл ./data/test-linedb-custom-json/id-first/users.jsonl")
fmt.Println(" Поле 'id' должно быть первым в JSON строке")
}