163 lines
5.7 KiB
Go
163 lines
5.7 KiB
Go
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"path/filepath"
|
||
"time"
|
||
|
||
"direct-dev.ru/gitea/GiteaAdmin/elowdb-go/pkg/linedb"
|
||
)
|
||
|
||
// Пример работы с партициями + индексируемой коллекцией.
|
||
//
|
||
// Запуск:
|
||
//
|
||
// go run ./examples/partitions/main.go
|
||
//
|
||
// Важно:
|
||
// - В текущей реализации индексы строятся для "обычных" коллекций.
|
||
// - Партиционированные коллекции (партиции) создаются динамически и сейчас не индексируются
|
||
// (см. getPartitionAdapter: JSONLFileOptions{CollectionName: partitionName} без IndexedFields).
|
||
func main() {
|
||
dbDir := filepath.Join(".", "examples", "partitions", "data")
|
||
_ = os.RemoveAll(dbDir)
|
||
if err := os.MkdirAll(dbDir, 0755); err != nil {
|
||
log.Fatalf("mkdir: %v", err)
|
||
}
|
||
|
||
// Настройка:
|
||
// - users: обычная коллекция с индексами
|
||
// - events: базовая коллекция для партиций (сама по себе не используется для записи),
|
||
// а реальные данные будут в events_<tenant>.jsonl
|
||
initOptions := &linedb.LineDbInitOptions{
|
||
DBFolder: dbDir,
|
||
Collections: []linedb.JSONLFileOptions{
|
||
{
|
||
CollectionName: "users",
|
||
AllocSize: 512,
|
||
IndexedFields: []string{"id", "email"},
|
||
},
|
||
{
|
||
CollectionName: "events",
|
||
AllocSize: 512,
|
||
IndexedFields: []string{"id", "type"},
|
||
},
|
||
},
|
||
Partitions: []linedb.PartitionCollection{
|
||
{
|
||
CollectionName: "events",
|
||
PartIDFn: func(v any) string {
|
||
m, ok := v.(map[string]any)
|
||
if !ok {
|
||
return "unknown"
|
||
}
|
||
tenant, ok := m["tenant"].(string)
|
||
if !ok || tenant == "" {
|
||
return "unknown"
|
||
}
|
||
return tenant
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
db := linedb.NewLineDb(&linedb.LineDbOptions{
|
||
IndexStore: linedb.NewInMemoryIndexStore(),
|
||
})
|
||
if err := db.Init(true, initOptions); err != nil {
|
||
log.Fatalf("Init failed: %v", err)
|
||
}
|
||
defer db.Close()
|
||
|
||
fmt.Println("=== 1) Индексируемая коллекция users ===")
|
||
users := []any{
|
||
map[string]any{"id": 1, "email": "a@example.com", "name": "Alice", "createdAt": time.Now().Format(time.RFC3339Nano)},
|
||
map[string]any{"id": 2, "email": "b@example.com", "name": "Bob", "createdAt": time.Now().Format(time.RFC3339Nano)},
|
||
}
|
||
// Используем Write + DoIndexing: true, чтобы индекс был актуален сразу после записи.
|
||
if err := db.Write(users, "users", linedb.LineDbAdapterOptions{DoIndexing: true}); err != nil {
|
||
log.Fatalf("Write users failed: %v", err)
|
||
}
|
||
|
||
byEmail, err := db.ReadByFilter(map[string]any{"email": "a@example.com"}, "users", linedb.LineDbAdapterOptions{})
|
||
if err != nil {
|
||
log.Fatalf("ReadByFilter users by email failed: %v", err)
|
||
}
|
||
mustLen("users by email", byEmail, 1)
|
||
|
||
byID, err := db.ReadByFilter(map[string]any{"id": 2}, "users", linedb.LineDbAdapterOptions{})
|
||
if err != nil {
|
||
log.Fatalf("ReadByFilter users by id failed: %v", err)
|
||
}
|
||
mustLen("users by id", byID, 1)
|
||
|
||
fmt.Println("OK: users inserted and searchable by indexed fields (id/email).")
|
||
|
||
fmt.Println("\n=== 2) Партиционированная коллекция events (events_<tenant>) ===")
|
||
events := []any{
|
||
map[string]any{"id": 1, "tenant": "A", "type": "signup", "status": "new", "ts": time.Now().Unix()},
|
||
map[string]any{"id": 2, "tenant": "A", "type": "purchase", "status": "new", "ts": time.Now().Unix()},
|
||
map[string]any{"id": 3, "tenant": "B", "type": "signup", "status": "new", "ts": time.Now().Unix()},
|
||
}
|
||
if err := db.Insert(events, "events", linedb.LineDbAdapterOptions{}); err != nil {
|
||
log.Fatalf("Insert events failed: %v", err)
|
||
}
|
||
|
||
tenantA, err := db.ReadByFilter(map[string]any{"type": "signup", "tenant": "A"}, "events", linedb.LineDbAdapterOptions{})
|
||
if err != nil {
|
||
log.Fatalf("ReadByFilter events tenant A failed: %v", err)
|
||
}
|
||
mustLen("events tenant A after insert", tenantA, 2)
|
||
|
||
tenantB, err := db.ReadByFilter(map[string]any{"type": "signup", "tenant": "B"}, "events", linedb.LineDbAdapterOptions{})
|
||
if err != nil {
|
||
log.Fatalf("ReadByFilter events tenant B failed: %v", err)
|
||
}
|
||
mustLen("events tenant B after insert", tenantB, 1)
|
||
|
||
fmt.Println("OK: события разложены по партициям (A и B).")
|
||
|
||
fmt.Println("\n=== 3) Update по всем партициям ===")
|
||
updated, err := db.Update(
|
||
map[string]any{"status": "processed"},
|
||
"events",
|
||
map[string]any{"tenant": "A"},
|
||
linedb.LineDbAdapterOptions{},
|
||
)
|
||
if err != nil {
|
||
log.Fatalf("Update events failed: %v", err)
|
||
}
|
||
mustLen("updated events for tenant A", updated, 2)
|
||
|
||
processedA, err := db.ReadByFilter(map[string]any{"tenant": "A", "status": "processed"}, "events", linedb.LineDbAdapterOptions{})
|
||
if err != nil {
|
||
log.Fatalf("ReadByFilter processed events failed: %v", err)
|
||
}
|
||
mustLen("processed events for tenant A", processedA, 2)
|
||
|
||
fmt.Println("OK: обновление затронуло записи в партиции A.")
|
||
|
||
fmt.Println("\n=== 4) Delete по всем партициям ===")
|
||
deleted, err := db.Delete(map[string]any{"id": 3}, "events", linedb.LineDbAdapterOptions{})
|
||
if err != nil {
|
||
log.Fatalf("Delete events failed: %v", err)
|
||
}
|
||
mustLen("deleted events id=3", deleted, 1)
|
||
|
||
allRemaining, err := db.ReadByFilter(nil, "events", linedb.LineDbAdapterOptions{})
|
||
if err != nil {
|
||
log.Fatalf("ReadByFilter all remaining events failed: %v", err)
|
||
}
|
||
mustLen("remaining events after delete", allRemaining, 2)
|
||
|
||
fmt.Printf("\nГотово. Данные примера в: %s\n", dbDir)
|
||
}
|
||
|
||
func mustLen(label string, got []any, want int) {
|
||
if len(got) != want {
|
||
log.Fatalf("%s: expected %d, got %d (%v)", label, want, len(got), got)
|
||
}
|
||
}
|