init elowdb go-port commit

This commit is contained in:
41 changed files with 7273 additions and 0 deletions

View File

@@ -0,0 +1,162 @@
package main
import (
"encoding/json"
"fmt"
"testing"
"time"
"linedb/pkg/linedb"
)
func TestCustomJSONSerialization(t *testing.T) {
// Тестируем стандартный JSON
standardJSONMarshal := func(v any) ([]byte, error) {
return json.Marshal(v)
}
standardJSONUnmarshal := func(data []byte, v any) error {
return json.Unmarshal(data, v)
}
// Тестируем кастомный JSON с метаданными
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 {
dataBytes, err := json.Marshal(dataField)
if err != nil {
return err
}
return json.Unmarshal(dataBytes, v)
}
return json.Unmarshal(data, v)
}
// Тестируем функции сериализации
testData := map[string]any{
"id": 1,
"name": "test",
"age": 25,
}
// Тест стандартного JSON
standardData, err := standardJSONMarshal(testData)
if err != nil {
t.Fatalf("Standard JSON marshal failed: %v", err)
}
var standardResult map[string]any
if err := standardJSONUnmarshal(standardData, &standardResult); err != nil {
t.Fatalf("Standard JSON unmarshal failed: %v", err)
}
if standardResult["name"] != "test" {
t.Errorf("Expected 'test', got %v", standardResult["name"])
}
// Тест кастомного JSON
customData, err := customJSONMarshal(testData)
if err != nil {
t.Fatalf("Custom JSON marshal failed: %v", err)
}
var customResult map[string]any
if err := customJSONUnmarshal(customData, &customResult); err != nil {
t.Fatalf("Custom JSON unmarshal failed: %v", err)
}
if customResult["name"] != "test" {
t.Errorf("Expected 'test', got %v", customResult["name"])
}
fmt.Println("✓ Custom JSON serialization test passed")
}
func TestJSONLFileWithCustomSerialization(t *testing.T) {
// Создаем функции сериализации
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 {
dataBytes, err := json.Marshal(dataField)
if err != nil {
return err
}
return json.Unmarshal(dataBytes, v)
}
return json.Unmarshal(data, v)
}
// Создаем JSONLFile с кастомными функциями
options := linedb.JSONLFileOptions{
CollectionName: "test",
JSONMarshal: customJSONMarshal,
JSONUnmarshal: customJSONUnmarshal,
}
file := linedb.NewJSONLFile("./test-custom.jsonl", "", options)
// Инициализируем файл
if err := file.Init(true, linedb.LineDbAdapterOptions{}); err != nil {
t.Fatalf("Failed to init file: %v", err)
}
// Тестовые данные
testData := map[string]any{
"id": 1,
"name": "test_user",
"age": 25,
}
// Записываем данные
if err := file.Write(testData, linedb.LineDbAdapterOptions{}); err != nil {
t.Fatalf("Failed to write data: %v", err)
}
// Читаем данные
result, err := file.Read(linedb.LineDbAdapterOptions{})
if err != nil {
t.Fatalf("Failed to read data: %v", err)
}
if len(result) != 1 {
t.Fatalf("Expected 1 record, got %d", len(result))
}
if record, ok := result[0].(map[string]any); ok {
if record["name"] != "test_user" {
t.Errorf("Expected 'test_user', got %v", record["name"])
}
} else {
t.Fatal("Failed to cast result to map")
}
fmt.Println("✓ JSONLFile with custom serialization test passed")
}

View File

@@ -0,0 +1,537 @@
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 строке")
}

View File

@@ -0,0 +1 @@
{"data":{"age":25,"id":1,"name":"test_user"},"timestamp":1754969067,"version":"1.0"}

View File

@@ -0,0 +1 @@
{"id":2,"category":"Books","createdAt":1754974225,"inStock":false,"name":"ID First Product","price":123.45,"sellerId":1}

View File

@@ -0,0 +1 @@
{"id":1,"createdAt":1754974225,"email":"id_first@example.com","isActive":true,"role":"user","username":"id_first_user"}

View File

@@ -0,0 +1 @@
{"createdAt":1754974225,"email":"standard@example.com","id":2,"isActive":true,"role":"admin","username":"standard_user"}