Files
elowdb-go/examples/partitions/main.go
2026-04-07 15:04:38 +06:00

163 lines
5.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

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

package main
import (
"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)
}
}