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 строке") }