init elowdb go-port commit

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

203
examples/README.md Normal file
View File

@@ -0,0 +1,203 @@
# Примеры использования LineDb
Этот каталог содержит примеры использования библиотеки LineDb, переведенные с TypeScript тестов.
## Структура примеров
### 1. `basic/` - Базовые операции
Простой пример демонстрирующий основные CRUD операции:
- Создание базы данных
- Вставка данных
- Чтение данных
- Фильтрация
- Обновление и удаление
### 2. `delete/` - Операции удаления
Примеры на основе теста `LineDbv2.delete.vi.test.ts`:
- Удаление одной записи по ID
- Удаление по текстовому фильтру
- Удаление нескольких записей по массиву
- Удаление по частичному совпадению
- Удаление из партиционированных коллекций
- Обработка краевых случаев
- Тест производительности
### 3. `insert/` - Операции вставки
Примеры на основе теста `LineDbv2.insert.vi.test.ts`:
- Вставка одной записи
- Вставка массива записей
- Автоматическая генерация ID
- Вставка в партиционированные коллекции
- Проверка уникальности
- Работа с разными типами данных
- Массовая вставка
- Работа с несколькими коллекциями
### 4. `integration/` - Интеграционные тесты
Примеры на основе теста `LineDbv2.integration.vi.test.ts`:
- Базовые CRUD операции
- Сложные запросы и фильтрация
- Работа с несколькими коллекциями
- Тест производительности
- HTTP API сервер
### 5. `custom-json/` - Настраиваемые функции сериализации JSON
Демонстрирует использование настраиваемых функций сериализации и десериализации JSON:
- Использование go-json (по умолчанию)
- Использование стандартного encoding/json
- Кастомные функции сериализации с дополнительной логикой
## Запуск примеров
### Базовый пример
```bash
cd examples/basic
go run main.go
```
### Примеры удаления
```bash
cd examples/delete
go run main.go
```
### Примеры вставки
```bash
cd examples/insert
go run main.go
```
### Интеграционные тесты
```bash
cd examples/integration
go run main.go
```
### Настраиваемые функции JSON
```bash
cd examples/custom-json
go run main.go
```
## Особенности примеров
### Партиционирование
Примеры демонстрируют работу с партиционированными коллекциями, где данные автоматически распределяются по файлам на основе значения определенного поля.
### Кэширование
Показана работа с кэшем для улучшения производительности чтения данных.
### Фильтрация
Демонстрируются различные способы фильтрации:
- Объектные фильтры
- Строковые фильтры
- Частичное совпадение
- Строгое сравнение
### Производительность
Примеры включают тесты производительности для массовых операций.
### HTTP API
Интеграционный пример включает простой HTTP сервер с REST API для работы с базой данных.
### Настраиваемые функции сериализации JSON
Библиотека поддерживает настраиваемые функции сериализации и десериализации JSON:
```go
// Функции сериализации по умолчанию (используют go-json)
func defaultJSONMarshal(v any) ([]byte, error) {
return json.Marshal(v)
}
func defaultJSONUnmarshal(data []byte, v any) error {
return json.Unmarshal(data, v)
}
// Настройка в опциях коллекции
initOptions := &linedb.LineDbInitOptions{
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
JSONMarshal: customMarshalFunction,
JSONUnmarshal: customUnmarshalFunction,
},
},
}
```
По умолчанию используется библиотека [go-json](https://github.com/goccy/go-json), но вы можете указать любые функции сериализации, соответствующие сигнатурам:
- `func(any) ([]byte, error)` для Marshal
- `func([]byte, any) error` для Unmarshal
## Структуры данных
Примеры используют следующие основные структуры данных:
```go
// Пользователь
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"`
}
// Продукт
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"`
}
// Заказ
type Order struct {
ID int `json:"id"`
UserID int `json:"userId"`
ProductID int `json:"productId"`
Quantity int `json:"quantity"`
TotalPrice float64 `json:"totalPrice"`
Status string `json:"status"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
}
```
## Конфигурация
Каждый пример создает свою тестовую папку и настраивает базу данных с соответствующими коллекциями и индексами. После выполнения примеры автоматически очищают тестовые данные.
## Примечания
- Все примеры написаны на русском языке для лучшего понимания
- Примеры демонстрируют реальные сценарии использования
- Код включает обработку ошибок и логирование
- Производительность измеряется и выводится в консоль

124
examples/basic/main.go Normal file
View File

@@ -0,0 +1,124 @@
package main
import (
"fmt"
"log"
"time"
"linedb/pkg/linedb"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
Created string `json:"created"`
}
func main() {
// Создаем опции инициализации
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 5 * time.Minute,
DBFolder: "./data",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 512,
IndexedFields: []string{"id", "email", "name"},
},
},
}
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
if err := db.Init(false, initOptions); err != nil {
log.Fatalf("Failed to init database: %v", err)
}
defer db.Close()
// Создаем тестовых пользователей
users := []any{
map[string]any{
"name": "John Doe",
"email": "john@example.com",
"age": 30,
"created": time.Now().Format(time.RFC3339),
},
map[string]any{
"name": "Jane Smith",
"email": "jane@example.com",
"age": 25,
"created": time.Now().Format(time.RFC3339),
},
map[string]any{
"name": "Bob Johnson",
"email": "bob@example.com",
"age": 35,
"created": time.Now().Format(time.RFC3339),
},
}
// Вставляем пользователей
if err := db.Insert(users, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Fatalf("Failed to insert users: %v", err)
}
fmt.Println("Users inserted successfully!")
// Читаем всех пользователей
allUsers, err := db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Fatalf("Failed to read users: %v", err)
}
fmt.Printf("Total users: %d\n", len(allUsers))
for i, user := range allUsers {
fmt.Printf("User %d: %+v\n", i+1, user)
}
// Ищем пользователя по email
filter := map[string]any{"email": "john@example.com"}
foundUsers, err := db.ReadByFilter(filter, "users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Fatalf("Failed to filter users: %v", err)
}
fmt.Printf("Found %d users with email john@example.com\n", len(foundUsers))
for _, user := range foundUsers {
fmt.Printf("Found user: %+v\n", user)
}
// Обновляем пользователя
updateData := map[string]any{"age": 31}
updateFilter := map[string]any{"email": "john@example.com"}
updatedUsers, err := db.Update(updateData, "users", updateFilter, linedb.LineDbAdapterOptions{})
if err != nil {
log.Fatalf("Failed to update user: %v", err)
}
fmt.Printf("Updated %d users\n", len(updatedUsers))
// Удаляем пользователя
deleteFilter := map[string]any{"email": "bob@example.com"}
deletedUsers, err := db.Delete(deleteFilter, "users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Fatalf("Failed to delete user: %v", err)
}
fmt.Printf("Deleted %d users\n", len(deletedUsers))
// Читаем оставшихся пользователей
remainingUsers, err := db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Fatalf("Failed to read remaining users: %v", err)
}
fmt.Printf("Remaining users: %d\n", len(remainingUsers))
for i, user := range remainingUsers {
fmt.Printf("Remaining user %d: %+v\n", i+1, user)
}
}

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"}

641
examples/delete/delete.go Normal file
View File

@@ -0,0 +1,641 @@
package main
import (
"fmt"
"log"
"time"
"linedb/pkg/linedb"
)
// testDeleteOneRecordByID тестирует удаление одной записи по ID
func testDeleteOneRecordByID() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-delete",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
},
},
}
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": "test_user",
"email": "test@example.com",
"isActive": true,
}
// Вставляем данные
if err := db.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert user: %v", err)
return
}
// Читаем данные
users, err := db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
fmt.Printf(" Записей до удаления: %d\n", len(users))
// Удаляем запись по ID
deleted, err := db.Delete(map[string]any{"id": 1}, "users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete user: %v", err)
return
}
fmt.Printf(" Удалено записей: %d\n", len(deleted))
// Проверяем результат
users, err = db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users after delete: %v", err)
return
}
fmt.Printf(" Записей после удаления: %d\n", len(users))
}
// testDeleteOneRecordByTextFilter тестирует удаление одной записи по текстовому фильтру
func testDeleteOneRecordByTextFilter() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-delete",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "products",
AllocSize: 256,
IndexedFields: []string{"id", "name"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем тестовые данные
product := map[string]any{
"id": 1,
"name": "Test Product",
"price": 99.99,
"category": "Electronics",
}
// Вставляем данные
if err := db.Insert(product, "products", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert product: %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(products))
// Удаляем запись по текстовому фильтру
deleted, err := db.Delete("name == 'Test Product'", "products", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete product: %v", err)
return
}
fmt.Printf(" Удалено записей: %d\n", len(deleted))
// Проверяем результат
products, err = db.Read("products", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read products after delete: %v", err)
return
}
fmt.Printf(" Записей после удаления: %d\n", len(products))
}
// testDeleteMultipleRecordsByArray тестирует удаление нескольких записей по массиву данных
func testDeleteMultipleRecordsByArray() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-delete",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "orders",
AllocSize: 256,
IndexedFields: []string{"id", "status"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем массив тестовых данных
orders := []any{
map[string]any{
"id": 1,
"status": "pending",
"amount": 100.0,
"customerId": 1,
},
map[string]any{
"id": 2,
"status": "completed",
"amount": 200.0,
"customerId": 2,
},
map[string]any{
"id": 3,
"status": "pending",
"amount": 150.0,
"customerId": 1,
},
}
// Вставляем данные
if err := db.Insert(orders, "orders", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert orders: %v", err)
return
}
// Читаем данные
ordersData, err := db.Read("orders", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read orders: %v", err)
return
}
fmt.Printf(" Записей до удаления: %d\n", len(ordersData))
// Удаляем несколько записей по массиву фильтров
deleteFilters := []any{
map[string]any{"id": 1},
map[string]any{"id": 2},
}
deleted, err := db.Delete(deleteFilters, "orders", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete orders: %v", err)
return
}
fmt.Printf(" Удалено записей: %d\n", len(deleted))
// Проверяем результат
ordersData, err = db.Read("orders", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read orders after delete: %v", err)
return
}
fmt.Printf(" Записей после удаления: %d\n", len(ordersData))
}
// testDeleteByPartialMatch тестирует удаление записей по частичному совпадению
func testDeleteByPartialMatch() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-delete",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "customers",
AllocSize: 256,
IndexedFields: []string{"id", "name"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем массив тестовых данных
customers := []any{
map[string]any{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
},
map[string]any{
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
},
map[string]any{
"id": 3,
"name": "John Smith",
"email": "john.smith@example.com",
},
}
// Вставляем данные
if err := db.Insert(customers, "customers", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert customers: %v", err)
return
}
// Читаем данные
customersData, err := db.Read("customers", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read customers: %v", err)
return
}
fmt.Printf(" Записей до удаления: %d\n", len(customersData))
// Удаляем записи по частичному совпадению
deleted, err := db.Delete(
map[string]any{"name": "John"},
"customers",
linedb.LineDbAdapterOptions{StrictCompare: false},
)
if err != nil {
log.Printf("Failed to delete customers: %v", err)
return
}
fmt.Printf(" Удалено записей с именем 'John': %d\n", len(deleted))
// Проверяем результат
customersData, err = db.Read("customers", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read customers after delete: %v", err)
return
}
fmt.Printf(" Записей после удаления: %d\n", len(customersData))
}
// testDeleteFromPartitionedCollections тестирует удаление из партиционированных коллекций
func testDeleteFromPartitionedCollections() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных с партициями
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-delete",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "logs",
AllocSize: 256,
IndexedFields: []string{"id", "level"},
},
},
Partitions: []linedb.PartitionCollection{
{
CollectionName: "logs",
PartIDFnStr: "level",
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем логи для разных уровней
logs := []any{
map[string]any{
"id": 1,
"level": "error",
"message": "Critical error occurred",
"timestamp": time.Now().Unix(),
},
map[string]any{
"id": 2,
"level": "info",
"message": "User logged in",
"timestamp": time.Now().Unix(),
},
map[string]any{
"id": 3,
"level": "error",
"message": "Another error",
"timestamp": time.Now().Unix(),
},
}
// Вставляем данные
if err := db.Insert(logs, "logs", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert logs: %v", err)
return
}
// Читаем данные
logsData, err := db.Read("logs", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read logs: %v", err)
return
}
fmt.Printf(" Записей до удаления: %d\n", len(logsData))
// Удаляем логи с уровнем 'error'
deleted, err := db.Delete(map[string]any{"level": "error"}, "logs", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete error logs: %v", err)
return
}
fmt.Printf(" Удалено записей с уровнем 'error': %d\n", len(deleted))
// Проверяем результат
logsData, err = db.Read("logs", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read logs after delete: %v", err)
return
}
fmt.Printf(" Записей после удаления: %d\n", len(logsData))
}
// testDeleteEdgeCases тестирует граничные случаи удаления
func testDeleteEdgeCases() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-delete",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "test_data",
AllocSize: 256,
IndexedFields: []string{"id", "type"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Тест удаления несуществующей записи
fmt.Println(" Тест удаления несуществующей записи:")
deleted, err := db.Delete(map[string]any{"id": 999}, "test_data", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete non-existent record: %v", err)
return
}
fmt.Printf(" Удалено несуществующих записей: %d\n", len(deleted))
// Тест удаления пустого массива
fmt.Println(" Тест удаления пустого массива:")
deleted, err = db.Delete([]any{}, "test_data", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete empty array: %v", err)
return
}
fmt.Printf(" Удалено записей из пустого массива: %d\n", len(deleted))
// Тест удаления записей с разными типами ID
fmt.Println(" Тест удаления записей с разными типами ID:")
// Вставляем запись со строковым ID
stringIDData := map[string]any{
"id": "user-1",
"name": "Test String Id User",
"type": "string_id",
}
if err := db.Insert(stringIDData, "test_data", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert string ID data: %v", err)
return
}
// Удаляем запись со строковым ID
deleted, err = db.Delete("id == 'user-1'", "test_data", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete string ID record: %v", err)
return
}
fmt.Printf(" Удалено записей со строковым ID: %d\n", len(deleted))
}
// testDeletePerformance тестирует производительность удаления
func testDeletePerformance() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 10000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-delete",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "performance_data",
AllocSize: 1024,
IndexedFields: []string{"id", "category"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем большое количество записей
recordCount := 1000
fmt.Printf(" Создание %d записей для теста производительности...\n", recordCount)
largeDataArray := make([]any, recordCount)
for i := 1; i <= recordCount; i++ {
largeDataArray[i-1] = map[string]any{
"id": i,
"name": fmt.Sprintf("User %d", i),
"category": fmt.Sprintf("Category %d", (i-1)%10+1),
"value": float64(i) * 1.5,
}
}
// Вставляем данные
if err := db.Insert(largeDataArray, "performance_data", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert large data array: %v", err)
return
}
// Читаем данные для проверки
result, err := db.Read("performance_data", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read large data: %v", err)
return
}
fmt.Printf(" Записей в базе: %d\n", len(result))
// Удаляем половину записей (четные ID)
startTime := time.Now()
itemsToDelete := make([]any, recordCount/2)
for i := 0; i < recordCount/2; i++ {
itemsToDelete[i] = map[string]any{"id": (i + 1) * 2}
}
deleted, err := db.Delete(itemsToDelete, "performance_data", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete large number of records: %v", err)
return
}
endTime := time.Now()
duration := endTime.Sub(startTime)
fmt.Printf(" Удалено записей: %d\n", len(deleted))
fmt.Printf(" Время выполнения: %v\n", duration)
// Проверяем результат
result, err = db.Read("performance_data", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read data after bulk delete: %v", err)
return
}
fmt.Printf(" Записей после массового удаления: %d\n", len(result))
}
// testDeleteMultipleCollections тестирует удаление из множественных коллекций
func testDeleteMultipleCollections() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных с множественными коллекциями
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-delete",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
},
{
CollectionName: "posts",
AllocSize: 256,
IndexedFields: []string{"id", "authorId"},
},
{
CollectionName: "comments",
AllocSize: 256,
IndexedFields: []string{"id", "postId"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем данные для разных коллекций
users := []any{
map[string]any{
"id": 1,
"username": "user1",
"email": "user1@example.com",
},
map[string]any{
"id": 2,
"username": "user2",
"email": "user2@example.com",
},
}
posts := []any{
map[string]any{
"id": 1,
"title": "First Post",
"authorId": 1,
"content": "This is the first post",
},
map[string]any{
"id": 2,
"title": "Second Post",
"authorId": 2,
"content": "This is the second post",
},
}
comments := []any{
map[string]any{
"id": 1,
"postId": 1,
"content": "Great post!",
"authorId": 2,
},
map[string]any{
"id": 2,
"postId": 2,
"content": "Nice article",
"authorId": 1,
},
}
// Вставляем данные в разные коллекции
if err := db.Insert(users, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert users: %v", err)
return
}
if err := db.Insert(posts, "posts", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert posts: %v", err)
return
}
if err := db.Insert(comments, "comments", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert comments: %v", err)
return
}
// Удаляем данные из разных коллекций
usersDeleted, _ := db.Delete(map[string]any{"id": 1}, "users", linedb.LineDbAdapterOptions{})
postsDeleted, _ := db.Delete(map[string]any{"authorId": 1}, "posts", linedb.LineDbAdapterOptions{})
commentsDeleted, _ := db.Delete(map[string]any{"postId": 1}, "comments", linedb.LineDbAdapterOptions{})
// Читаем данные из всех коллекций
usersData, _ := db.Read("users", linedb.LineDbAdapterOptions{})
postsData, _ := db.Read("posts", linedb.LineDbAdapterOptions{})
commentsData, _ := db.Read("comments", linedb.LineDbAdapterOptions{})
fmt.Printf(" Удалено пользователей: %d\n", len(usersDeleted))
fmt.Printf(" Удалено постов: %d\n", len(postsDeleted))
fmt.Printf(" Удалено комментариев: %d\n", len(commentsDeleted))
fmt.Printf(" Осталось пользователей: %d\n", len(usersData))
fmt.Printf(" Осталось постов: %d\n", len(postsData))
fmt.Printf(" Осталось комментариев: %d\n", len(commentsData))
}

167
examples/delete/main.go Normal file
View File

@@ -0,0 +1,167 @@
package main
import (
"fmt"
"os"
"strconv"
"strings"
)
// TestData представляет тестовые данные
type TestData struct {
ID any `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Value *int `json:"value,omitempty"`
UserID int `json:"userId"`
Timestamp int64 `json:"timestamp"`
}
// TestUser представляет пользователя
type TestUser struct {
ID int `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
IsActive bool `json:"isActive"`
Role string `json:"role"`
Timestamp int64 `json:"timestamp"`
}
// TestOrder представляет заказ
type TestOrder struct {
ID int `json:"id"`
UserID int `json:"userId"`
Status string `json:"status"`
Amount float64 `json:"amount"`
Timestamp int64 `json:"timestamp"`
}
func main() {
// Очищаем тестовую папку
os.RemoveAll("./data/test-linedb-delete")
fmt.Println("=== LineDb Delete Operations 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 > 8 {
fmt.Printf("Ошибка: номер теста %d должен быть от 1 до 8\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 - Удаление одной записи по ID")
fmt.Println(" 2 - Удаление одной записи по текстовому фильтру")
fmt.Println(" 3 - Удаление нескольких записей по массиву данных")
fmt.Println(" 4 - Удаление записей по частичному совпадению")
fmt.Println(" 5 - Удаление записей из партиционированных коллекций")
fmt.Println(" 6 - Удаление записей с граничными случаями")
fmt.Println(" 7 - Удаление записей с производительностью")
fmt.Println(" 8 - Удаление записей с множественными коллекциями")
os.Exit(0)
}
// runAllTests запускает все тесты
func runAllTests() {
testDeleteOneRecordByID()
testDeleteOneRecordByTextFilter()
testDeleteMultipleRecordsByArray()
testDeleteByPartialMatch()
testDeleteFromPartitionedCollections()
testDeleteEdgeCases()
testDeletePerformance()
testDeleteMultipleCollections()
}
// runSelectedTests запускает только выбранные тесты
func runSelectedTests(numbers []int) {
for _, num := range numbers {
switch num {
case 1:
fmt.Println("1. Удаление одной записи по ID")
testDeleteOneRecordByID()
case 2:
fmt.Println("2. Удаление одной записи по текстовому фильтру")
testDeleteOneRecordByTextFilter()
case 3:
fmt.Println("3. Удаление нескольких записей по массиву данных")
testDeleteMultipleRecordsByArray()
case 4:
fmt.Println("4. Удаление записей по частичному совпадению")
testDeleteByPartialMatch()
case 5:
fmt.Println("5. Удаление записей из партиционированных коллекций")
testDeleteFromPartitionedCollections()
case 6:
fmt.Println("6. Удаление записей с граничными случаями")
testDeleteEdgeCases()
case 7:
fmt.Println("7. Удаление записей с производительностью")
testDeletePerformance()
case 8:
fmt.Println("8. Удаление записей с множественными коллекциями")
testDeleteMultipleCollections()
}
}
}

592
examples/insert/insert.go Normal file
View File

@@ -0,0 +1,592 @@
package main
import (
"fmt"
"log"
"time"
"linedb/pkg/linedb"
)
// testSingleInsert тестирует вставку одной записи
func testSingleInsert() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-insert",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
},
},
}
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": "john_doe",
"email": "john@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
}
// Читаем данные
users, err := db.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
fmt.Printf(" Пользователей в базе: %d\n", len(users))
if len(users) > 0 {
if record, ok := users[0].(map[string]any); ok {
fmt.Printf(" ID: %v, Username: %s\n", record["id"], record["username"])
}
}
}
// testArrayInsert тестирует вставку массива записей
func testArrayInsert() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-insert",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "products",
AllocSize: 256,
IndexedFields: []string{"id", "name"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем массив продуктов
products := []any{
map[string]any{
"id": 1,
"name": "Laptop",
"price": 999.99,
"category": "Electronics",
"inStock": true,
},
map[string]any{
"id": 2,
"name": "Mouse",
"price": 29.99,
"category": "Electronics",
"inStock": true,
},
map[string]any{
"id": 3,
"name": "Keyboard",
"price": 79.99,
"category": "Electronics",
"inStock": false,
},
}
// Вставляем данные
if err := db.Insert(products, "products", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert products: %v", err)
return
}
// Читаем данные
productsData, err := db.Read("products", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read products: %v", err)
return
}
fmt.Printf(" Продуктов в базе: %d\n", len(productsData))
for i, product := range productsData {
if record, ok := product.(map[string]any); ok {
fmt.Printf(" %d. ID: %v, Name: %s, Price: %v\n", i+1, record["id"], record["name"], record["price"])
}
}
}
// testAutoID тестирует автоматическую генерацию ID
func testAutoID() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-insert",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "orders",
AllocSize: 256,
IndexedFields: []string{"id", "customerId"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем заказы без ID
orders := []any{
map[string]any{
"customerId": 1,
"amount": 150.00,
"status": "pending",
"createdAt": time.Now().Unix(),
},
map[string]any{
"customerId": 2,
"amount": 75.50,
"status": "completed",
"createdAt": time.Now().Unix(),
},
}
// Вставляем данные
if err := db.Insert(orders, "orders", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert orders: %v", err)
return
}
// Читаем данные
ordersData, err := db.Read("orders", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read orders: %v", err)
return
}
fmt.Printf(" Заказов в базе: %d\n", len(ordersData))
for i, order := range ordersData {
if record, ok := order.(map[string]any); ok {
fmt.Printf(" %d. ID: %v, Customer: %v, Amount: %v\n", i+1, record["id"], record["customerId"], record["amount"])
}
}
}
// testPartitionedCollections тестирует партиционированные коллекции
func testPartitionedCollections() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных с партиционированными коллекциями
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-insert",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users_2024",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
},
{
CollectionName: "users_2023",
AllocSize: 256,
IndexedFields: []string{"id", "username"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем пользователей для разных партиций
users2024 := []any{
map[string]any{
"id": 1,
"username": "user_2024_1",
"email": "user1@2024.com",
"year": 2024,
},
map[string]any{
"id": 2,
"username": "user_2024_2",
"email": "user2@2024.com",
"year": 2024,
},
}
users2023 := []any{
map[string]any{
"id": 3,
"username": "user_2023_1",
"email": "user1@2023.com",
"year": 2023,
},
}
// Вставляем данные в разные партиции
if err := db.Insert(users2024, "users_2024", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert users 2024: %v", err)
return
}
if err := db.Insert(users2023, "users_2023", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert users 2023: %v", err)
return
}
// Читаем данные из обеих партиций
users2024Data, err := db.Read("users_2024", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users 2024: %v", err)
return
}
users2023Data, err := db.Read("users_2023", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users 2023: %v", err)
return
}
fmt.Printf(" Пользователей 2024: %d\n", len(users2024Data))
fmt.Printf(" Пользователей 2023: %d\n", len(users2023Data))
}
// testUniquenessCheck тестирует проверку уникальности
func testUniquenessCheck() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-insert",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "unique_users",
AllocSize: 256,
IndexedFields: []string{"id", "email"},
},
},
}
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": "unique_user",
"email": "unique@example.com",
"isActive": true,
}
// Вставляем данные
if err := db.Insert(user, "unique_users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert user: %v", err)
return
}
// Пытаемся вставить пользователя с тем же email
duplicateUser := map[string]any{
"id": 2,
"username": "duplicate_user",
"email": "unique@example.com", // Тот же email
"isActive": true,
}
if err := db.Insert(duplicateUser, "unique_users", linedb.LineDbAdapterOptions{}); err != nil {
fmt.Printf(" Ожидаемая ошибка дублирования: %v\n", err)
} else {
fmt.Printf(" Дублирование не обнаружено\n")
}
// Читаем данные
users, err := db.Read("unique_users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
fmt.Printf(" Уникальных пользователей: %d\n", len(users))
}
// testDifferentDataTypes тестирует различные типы данных
func testDifferentDataTypes() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-insert",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "mixed_data",
AllocSize: 256,
IndexedFields: []string{"id", "type"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем данные разных типов
mixedData := []any{
map[string]any{
"id": 1,
"type": "string",
"value": "Hello World",
"isString": true,
},
map[string]any{
"id": 2,
"type": "number",
"value": 42.5,
"isNumber": true,
},
map[string]any{
"id": 3,
"type": "boolean",
"value": true,
"isBool": true,
},
map[string]any{
"id": 4,
"type": "array",
"value": []any{1, 2, 3, "four"},
"isArray": true,
},
map[string]any{
"id": 5,
"type": "object",
"value": map[string]any{"nested": "value", "count": 10},
"isObject": true,
},
}
// Вставляем данные
if err := db.Insert(mixedData, "mixed_data", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert mixed data: %v", err)
return
}
// Читаем данные
data, err := db.Read("mixed_data", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read mixed data: %v", err)
return
}
fmt.Printf(" Записей с разными типами данных: %d\n", len(data))
for i, record := range data {
if item, ok := record.(map[string]any); ok {
fmt.Printf(" %d. ID: %v, Type: %s\n", i+1, item["id"], item["type"])
}
}
}
// testBulkInsert тестирует массовую вставку
func testBulkInsert() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных
initOptions := &linedb.LineDbInitOptions{
CacheSize: 10000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-insert",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "bulk_items",
AllocSize: 1024,
IndexedFields: []string{"id", "category"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем большое количество записей
var bulkItems []any
for i := 1; i <= 1000; i++ {
item := map[string]any{
"id": i,
"name": fmt.Sprintf("Item %d", i),
"category": fmt.Sprintf("Category %d", (i-1)%10+1),
"price": float64(i) * 1.5,
"inStock": i%2 == 0,
"createdAt": time.Now().Unix(),
}
bulkItems = append(bulkItems, item)
}
// Вставляем данные
startTime := time.Now()
if err := db.Insert(bulkItems, "bulk_items", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert bulk items: %v", err)
return
}
insertTime := time.Since(startTime)
// Читаем данные
startTime = time.Now()
items, err := db.Read("bulk_items", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read bulk items: %v", err)
return
}
readTime := time.Since(startTime)
fmt.Printf(" Массовая вставка: %d записей за %v\n", len(items), insertTime)
fmt.Printf(" Чтение: %d записей за %v\n", len(items), readTime)
}
// testMultipleCollections тестирует работу с множественными коллекциями
func testMultipleCollections() {
// Создаем базу данных
db := linedb.NewLineDb(nil)
// Инициализируем базу данных с множественными коллекциями
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-insert",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "customers",
AllocSize: 256,
IndexedFields: []string{"id", "email"},
},
{
CollectionName: "orders",
AllocSize: 256,
IndexedFields: []string{"id", "customerId"},
},
{
CollectionName: "products",
AllocSize: 256,
IndexedFields: []string{"id", "name"},
},
},
}
if err := db.Init(true, initOptions); err != nil {
log.Printf("Failed to init database: %v", err)
return
}
defer db.Close()
// Создаем данные для разных коллекций
customers := []any{
map[string]any{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"isActive": true,
},
map[string]any{
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
"isActive": true,
},
}
orders := []any{
map[string]any{
"id": 1,
"customerId": 1,
"amount": 150.00,
"status": "completed",
},
map[string]any{
"id": 2,
"customerId": 2,
"amount": 75.50,
"status": "pending",
},
}
products := []any{
map[string]any{
"id": 1,
"name": "Laptop",
"price": 999.99,
"category": "Electronics",
},
map[string]any{
"id": 2,
"name": "Mouse",
"price": 29.99,
"category": "Electronics",
},
}
// Вставляем данные в разные коллекции
if err := db.Insert(customers, "customers", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert customers: %v", err)
return
}
if err := db.Insert(orders, "orders", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert orders: %v", err)
return
}
if err := db.Insert(products, "products", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert products: %v", err)
return
}
// Читаем данные из всех коллекций
customersData, _ := db.Read("customers", linedb.LineDbAdapterOptions{})
ordersData, _ := db.Read("orders", linedb.LineDbAdapterOptions{})
productsData, _ := db.Read("products", linedb.LineDbAdapterOptions{})
fmt.Printf(" Коллекций: 3\n")
fmt.Printf(" Клиентов: %d\n", len(customersData))
fmt.Printf(" Заказов: %d\n", len(ordersData))
fmt.Printf(" Продуктов: %d\n", len(productsData))
}

138
examples/insert/main.go Normal file
View File

@@ -0,0 +1,138 @@
package main
import (
"fmt"
"os"
"strconv"
"strings"
)
func main() {
// Очищаем тестовые данные
os.RemoveAll("./data/test-linedb-insert")
fmt.Println("=== LineDb Insert Operations 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 > 8 {
fmt.Printf("Ошибка: номер теста %d должен быть от 1 до 8\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 - Вставка одной записи")
fmt.Println(" 2 - Вставка массива записей")
fmt.Println(" 3 - Автоматическая генерация ID")
fmt.Println(" 4 - Партиционированные коллекции")
fmt.Println(" 5 - Проверка уникальности")
fmt.Println(" 6 - Различные типы данных")
fmt.Println(" 7 - Массовая вставка")
fmt.Println(" 8 - Множественные коллекции")
os.Exit(0)
}
// runAllTests запускает все тесты
func runAllTests() {
testSingleInsert()
testArrayInsert()
testAutoID()
testPartitionedCollections()
testUniquenessCheck()
testDifferentDataTypes()
testBulkInsert()
testMultipleCollections()
}
// runSelectedTests запускает только выбранные тесты
func runSelectedTests(numbers []int) {
for _, num := range numbers {
switch num {
case 1:
fmt.Println("1. Вставка одной записи")
testSingleInsert()
case 2:
fmt.Println("2. Вставка массива записей")
testArrayInsert()
case 3:
fmt.Println("3. Автоматическая генерация ID")
testAutoID()
case 4:
fmt.Println("4. Партиционированные коллекции")
testPartitionedCollections()
case 5:
fmt.Println("5. Проверка уникальности")
testUniquenessCheck()
case 6:
fmt.Println("6. Различные типы данных")
testDifferentDataTypes()
case 7:
fmt.Println("7. Массовая вставка")
testBulkInsert()
case 8:
fmt.Println("8. Множественные коллекции")
testMultipleCollections()
}
}
}

View File

@@ -0,0 +1 @@
{"createdAt":1754974407,"email":"john@example.com","id":1,"isActive":true,"role":"user","username":"john_doe"}

View File

@@ -0,0 +1,722 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"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"`
LastLogin *int64 `json:"lastLogin,omitempty"`
}
// 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"`
}
// Order представляет заказ
type Order struct {
ID int `json:"id"`
UserID int `json:"userId"`
ProductID int `json:"productId"`
Quantity int `json:"quantity"`
TotalPrice float64 `json:"totalPrice"`
Status string `json:"status"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
}
// OrderItem представляет элемент заказа
type OrderItem struct {
ID int `json:"id"`
OrderID int `json:"orderId"`
ProductID int `json:"productId"`
Quantity int `json:"quantity"`
Price float64 `json:"price"`
}
// APIResponse представляет ответ API
type APIResponse struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error string `json:"error,omitempty"`
Message string `json:"message,omitempty"`
}
var Database *linedb.LineDb
var server *http.Server
// 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 - Базовые CRUD операции")
fmt.Println(" 2 - Сложные запросы и фильтрация")
fmt.Println(" 3 - Работа с несколькими коллекциями")
fmt.Println(" 4 - Тест производительности")
fmt.Println(" 5 - HTTP API сервер")
fmt.Println(" 6 - API endpoints")
os.Exit(0)
}
// runAllTests запускает все тесты
func runAllTests() {
testBasicCRUDOperations()
testComplexQueries()
testMultipleCollections()
testPerformance()
testHTTPServer()
testAPIEndpoints()
}
// runSelectedTests запускает только выбранные тесты
func runSelectedTests(numbers []int) {
for _, num := range numbers {
switch num {
case 1:
fmt.Println("1. Базовые CRUD операции")
testBasicCRUDOperations()
case 2:
fmt.Println("2. Сложные запросы и фильтрация")
testComplexQueries()
case 3:
fmt.Println("3. Работа с несколькими коллекциями")
testMultipleCollections()
case 4:
fmt.Println("4. Тест производительности")
testPerformance()
case 5:
fmt.Println("5. HTTP API сервер")
testHTTPServer()
case 6:
fmt.Println("6. API endpoints")
testAPIEndpoints()
}
}
}
func testBasicCRUDOperations() {
// Создаем пользователя
user := map[string]any{
"id": 1,
"username": "testuser",
"email": "test@example.com",
"isActive": true,
"role": "user",
"createdAt": time.Now().Unix(),
}
// Вставка
if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert user: %v", err)
return
}
fmt.Printf("\tПользователь создан: %s\n", user["username"])
// Чтение
result, err := Database.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
fmt.Printf("\tПользователей в базе: %d\n", len(result))
// Обновление
updateData := map[string]any{"role": "admin"}
updated, err := Database.Update(updateData, "users", map[string]any{"id": 1}, linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to update user: %v", err)
return
}
fmt.Printf(" Обновлено пользователей: %d\n", len(updated))
// Удаление
deleted, err := Database.Delete(map[string]any{"id": 1}, "users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to delete user: %v", err)
return
}
fmt.Printf(" Удалено пользователей: %d\n", len(deleted))
// Проверяем, что пользователь удален
result, err = Database.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users after delete: %v", err)
return
}
fmt.Printf(" Пользователей после удаления: %d\n", len(result))
}
func testComplexQueries() {
// Создаем несколько пользователей
users := []any{
map[string]any{
"id": 1,
"username": "john_doe",
"email": "john@example.com",
"isActive": true,
"role": "user",
"createdAt": time.Now().Unix(),
},
map[string]any{
"id": 2,
"username": "jane_smith",
"email": "jane@example.com",
"isActive": true,
"role": "admin",
"createdAt": time.Now().Unix(),
},
map[string]any{
"id": 3,
"username": "bob_wilson",
"email": "bob@example.com",
"isActive": false,
"role": "user",
"createdAt": time.Now().Unix(),
},
}
if err := Database.Insert(users, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert users: %v", err)
return
}
// Фильтрация по роли
admins, err := Database.ReadByFilter(map[string]any{"role": "admin"}, "users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to filter admins: %v", err)
return
}
fmt.Printf(" Администраторов: %d\n", len(admins))
// Фильтрация по активности
activeUsers, err := Database.ReadByFilter(map[string]any{"isActive": true}, "users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to filter active users: %v", err)
return
}
fmt.Printf(" Активных пользователей: %d\n", len(activeUsers))
// Строковая фильтрация
emailFilter, err := Database.ReadByFilter("email == 'john@example.com'", "users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to filter by email: %v", err)
return
}
fmt.Printf(" Пользователей с email john@example.com: %d\n", len(emailFilter))
// Частичное совпадение
partialMatch, err := Database.ReadByFilter(
map[string]any{"username": "john"},
"users",
linedb.LineDbAdapterOptions{StrictCompare: false},
)
if err != nil {
log.Printf("Failed to partial match: %v", err)
return
}
fmt.Printf(" Пользователей с именем содержащим 'john': %d\n", len(partialMatch))
}
func testMultipleCollections() {
// Создаем пользователя
user := map[string]any{
"id": 1,
"username": "customer1",
"email": "customer1@example.com",
"isActive": true,
"role": "customer",
"createdAt": time.Now().Unix(),
}
// Создаем продукты
products := []any{
map[string]any{
"id": 1,
"name": "Laptop",
"price": 999.99,
"category": "Electronics",
"inStock": true,
"sellerId": 1,
"createdAt": time.Now().Unix(),
},
map[string]any{
"id": 2,
"name": "Mouse",
"price": 29.99,
"category": "Electronics",
"inStock": true,
"sellerId": 1,
"createdAt": time.Now().Unix(),
},
}
// Создаем заказ
order := map[string]any{
"id": 1,
"userId": 1,
"productId": 1,
"quantity": 2,
"totalPrice": 1999.98,
"status": "pending",
"createdAt": time.Now().Unix(),
"updatedAt": time.Now().Unix(),
}
// Создаем элементы заказа
orderItems := []any{
map[string]any{
"id": 1,
"orderId": 1,
"productId": 1,
"quantity": 2,
"price": 999.99,
},
}
// Вставляем данные во все коллекции
if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert user: %v", err)
return
}
if err := Database.Insert(products, "products", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert products: %v", err)
return
}
if err := Database.Insert(order, "orders", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert order: %v", err)
return
}
if err := Database.Insert(orderItems, "orderItems", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert order items: %v", err)
return
}
// Читаем данные из всех коллекций
users, err := Database.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
productsData, err := Database.Read("products", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read products: %v", err)
return
}
orders, err := Database.Read("orders", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read orders: %v", err)
return
}
orderItemsData, err := Database.Read("orderItems", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read order items: %v", err)
return
}
fmt.Printf(" Пользователей: %d\n", len(users))
fmt.Printf(" Продуктов: %d\n", len(productsData))
fmt.Printf(" Заказов: %d\n", len(orders))
fmt.Printf(" Элементов заказов: %d\n", len(orderItemsData))
// Сложный запрос: найти все заказы пользователя
userOrders, err := Database.ReadByFilter(map[string]any{"userId": 1}, "orders", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to get user orders: %v", err)
return
}
fmt.Printf(" Заказов пользователя 1: %d\n", len(userOrders))
}
func testPerformance() {
// Тест производительности массовой вставки
recordCount := 500
fmt.Printf(" Создание %d записей для теста производительности...\n", recordCount)
// Создаем пользователей
users := make([]any, recordCount)
for i := 1; i <= recordCount; i++ {
users[i-1] = map[string]any{
"id": i,
"username": fmt.Sprintf("user%d", i),
"email": fmt.Sprintf("user%d@example.com", i),
"isActive": i%2 == 0, // чередуем активных и неактивных
"role": "user",
"createdAt": time.Now().Unix(),
}
}
// Измеряем время вставки
startTime := time.Now()
if err := Database.Insert(users, "users", linedb.LineDbAdapterOptions{}); err != nil {
log.Printf("Failed to insert users: %v", err)
return
}
endTime := time.Now()
duration := endTime.Sub(startTime)
fmt.Printf(" Вставлено пользователей: %d\n", recordCount)
fmt.Printf(" Время выполнения: %v\n", duration)
fmt.Printf(" Скорость: %.2f записей/сек\n", float64(recordCount)/duration.Seconds())
// Тест производительности чтения
startTime = time.Now()
result, err := Database.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to read users: %v", err)
return
}
endTime = time.Now()
readDuration := endTime.Sub(startTime)
fmt.Printf(" Прочитано пользователей: %d\n", len(result))
fmt.Printf(" Время чтения: %v\n", readDuration)
fmt.Printf(" Скорость чтения: %.2f записей/сек\n", float64(len(result))/readDuration.Seconds())
// Тест производительности фильтрации
startTime = time.Now()
activeUsers, err := Database.ReadByFilter(map[string]any{"isActive": true}, "users", linedb.LineDbAdapterOptions{})
if err != nil {
log.Printf("Failed to filter active users: %v", err)
return
}
endTime = time.Now()
filterDuration := endTime.Sub(startTime)
fmt.Printf(" Активных пользователей: %d\n", len(activeUsers))
fmt.Printf(" Время фильтрации: %v\n", filterDuration)
}
func testHTTPServer() {
// Настраиваем HTTP сервер
mux := http.NewServeMux()
// Обработчики API
mux.HandleFunc("/api/users", handleUsers)
mux.HandleFunc("/api/products", handleProducts)
mux.HandleFunc("/api/orders", handleOrders)
server = &http.Server{
Addr: ":3001",
Handler: mux,
}
fmt.Printf(" Запуск HTTP сервера на порту 3001...\n")
fmt.Printf(" Доступные эндпоинты:\n")
fmt.Printf(" - GET /api/users - получить всех пользователей\n")
fmt.Printf(" - POST /api/users - создать пользователя\n")
fmt.Printf(" - GET /api/products - получить все продукты\n")
fmt.Printf(" - POST /api/products - создать продукт\n")
fmt.Printf(" - GET /api/orders - получить все заказы\n")
fmt.Printf(" - POST /api/orders - создать заказ\n")
// Запускаем сервер в горутине
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("HTTP server error: %v", err)
}
}()
// Даем серверу время на запуск
time.Sleep(1 * time.Second)
// Тестируем API
fmt.Printf(" Тестирование API...\n")
testAPIEndpoints()
// Останавливаем сервер
if err := server.Close(); err != nil {
log.Printf("Failed to close server: %v", err)
}
}
func handleUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case "GET":
users, err := Database.Read("users", linedb.LineDbAdapterOptions{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(APIResponse{Success: true, Data: users})
case "POST":
var user map[string]any
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "User created successfully"})
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func handleProducts(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case "GET":
products, err := Database.Read("products", linedb.LineDbAdapterOptions{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(APIResponse{Success: true, Data: products})
case "POST":
var product map[string]any
if err := json.NewDecoder(r.Body).Decode(&product); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := Database.Insert(product, "products", linedb.LineDbAdapterOptions{}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "Product created successfully"})
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func handleOrders(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case "GET":
orders, err := Database.Read("orders", linedb.LineDbAdapterOptions{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(APIResponse{Success: true, Data: orders})
case "POST":
var order map[string]any
if err := json.NewDecoder(r.Body).Decode(&order); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := Database.Insert(order, "orders", linedb.LineDbAdapterOptions{}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "Order created successfully"})
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func testAPIEndpoints() {
// Тестируем GET /api/users
resp, err := http.Get("http://localhost:3001/api/users")
if err != nil {
log.Printf("Failed to test GET /api/users: %v", err)
return
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
fmt.Printf(" ✓ GET /api/users работает\n")
} else {
fmt.Printf(" ✗ GET /api/users вернул статус %d\n", resp.StatusCode)
}
// Тестируем POST /api/users
userData := map[string]any{
"id": 999,
"username": "apitest",
"email": "apitest@example.com",
"isActive": true,
"role": "user",
"createdAt": time.Now().Unix(),
}
jsonData, _ := json.Marshal(userData)
resp, err = http.Post("http://localhost:3001/api/users", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
log.Printf("Failed to test POST /api/users: %v", err)
return
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
fmt.Printf(" ✓ POST /api/users работает\n")
} else {
fmt.Printf(" ✗ POST /api/users вернул статус %d\n", resp.StatusCode)
}
}
func main() {
// Очищаем тестовую папку
os.RemoveAll("./data/test-linedb-integration")
// Создаем опции инициализации
initOptions := &linedb.LineDbInitOptions{
CacheSize: 1000,
CacheTTL: 10 * time.Second,
DBFolder: "./data/test-linedb-integration",
Collections: []linedb.JSONLFileOptions{
{
CollectionName: "users",
AllocSize: 256,
IndexedFields: []string{"id", "username", "email"},
},
{
CollectionName: "products",
AllocSize: 256,
IndexedFields: []string{"id", "name", "category"},
},
{
CollectionName: "orders",
AllocSize: 256,
IndexedFields: []string{"id", "userId", "status"},
},
{
CollectionName: "orderItems",
AllocSize: 256,
IndexedFields: []string{"id", "orderId", "productId"},
},
},
Partitions: []linedb.PartitionCollection{
{
CollectionName: "orders",
PartIDFnStr: "userId",
},
},
}
// Создаем базу данных
Database = linedb.NewLineDb(nil)
// Инициализируем базу данных
if err := Database.Init(true, initOptions); err != nil {
log.Fatalf("Failed to init database: %v", err)
}
defer Database.Close()
fmt.Println("=== LineDb Integration Tests Demo ===")
numbers := parseTestNumbers()
if len(numbers) > 0 {
runSelectedTests(numbers)
} else {
runAllTests()
}
// // Тест 1: Базовые CRUD операции
// fmt.Println("1. Базовые CRUD операции")
// testBasicCRUDOperations()
// // Тест 2: Сложные запросы и фильтрация
// fmt.Println("\n2. Сложные запросы и фильтрация")
// testComplexQueries()
// // Тест 3: Работа с несколькими коллекциями
// fmt.Println("\n3. Работа с несколькими коллекциями")
// testMultipleCollections()
// // Тест 4: Производительность
// fmt.Println("\n4. Тест производительности")
// testPerformance()
// // Тест 5: HTTP API сервер
// fmt.Println("\n5. HTTP API сервер")
// testHTTPServer()
fmt.Println("\n=== Все тесты завершены ===")
}