.Live[T](table, diff)
Initiate a live query from a SurrealQL statement.
Method Syntax
db.Live[UUID](table, diff)
Arguments
| Arguments | Description |
|---|
sql required | Specifies the SurrealQL statements. |
cancellationToken optional | The cancellationToken enables graceful cancellation of asynchronous operations. |
Example usage
package main
import (
"context"
"fmt"
"sort"
"strings"
"time"
surrealdb "github.com/surrealdb/surrealdb.go"
"github.com/surrealdb/surrealdb.go/contrib/testenv"
"github.com/surrealdb/surrealdb.go/pkg/connection"
"github.com/surrealdb/surrealdb.go/pkg/models"
)
func formatRecordResult(record map[string]any) string {
keys := make([]string, 0, len(record))
for k := range record {
keys = append(keys, k)
}
sort.Strings(keys)
var parts []string
for _, k := range keys {
val := record[k]
if k == "id" {
recordID := val.(models.RecordID)
parts = append(parts, fmt.Sprintf("id=%s:`UUID`", recordID.Table))
} else {
parts = append(parts, fmt.Sprintf("%s=%v", k, val))
}
}
return "{" + strings.Join(parts, " ") + "}"
}
func formatDiffResult(diffs []any) string {
var items []string
for _, item := range diffs {
diffOp, ok := item.(map[string]any)
if !ok {
panic(fmt.Sprintf("Expected diff operation to be map[string]any, got %T", item))
}
items = append(items, formatDiffOperation(diffOp))
}
return "[" + strings.Join(items, " ") + "]"
}
func formatPatchDataMap(data map[string]any) string {
keys := make([]string, 0, len(data))
for k := range data {
keys = append(keys, k)
}
sort.Strings(keys)
var parts []string
for _, k := range keys {
val := data[k]
if k == "id" {
recordID := val.(models.RecordID)
parts = append(parts, fmt.Sprintf("id=%s:`UUID`", recordID.Table))
} else {
parts = append(parts, fmt.Sprintf("%s=%v", k, val))
}
}
return "{" + strings.Join(parts, " ") + "}"
}
func formatDiffOperation(op map[string]any) string {
keys := make([]string, 0, len(op))
for k := range op {
keys = append(keys, k)
}
sort.Strings(keys)
var parts []string
for _, k := range keys {
val := op[k]
if k == "value" {
if patchData, ok := val.(map[string]any); ok {
parts = append(parts, fmt.Sprintf("value=%s", formatPatchDataMap(patchData)))
} else {
parts = append(parts, fmt.Sprintf("value=%v", val))
}
} else {
parts = append(parts, fmt.Sprintf("%s=%v", k, val))
}
}
return "{" + strings.Join(parts, " ") + "}"
}
func ExampleLive() {
config := testenv.MustNewConfig("surrealdbexamples", "livequery_rpc", "users")
config.Endpoint = testenv.GetSurrealDBWSURL()
db := config.MustNew()
type User struct {
ID *models.RecordID `json:"id,omitempty"`
Username string `json:"username"`
Email string `json:"email"`
}
ctx := context.Background()
live, err := surrealdb.Live(ctx, db, "users", false)
if err != nil {
panic(fmt.Sprintf("Failed to start live query: %v", err))
}
fmt.Println("Started live query")
notifications, err := db.LiveNotifications(live.String())
if err != nil {
panic(fmt.Sprintf("Failed to get live notifications channel: %v", err))
}
received := make(chan struct{})
done := make(chan bool)
go func() {
for notification := range notifications {
record, ok := notification.Result.(map[string]any)
if !ok {
panic(fmt.Sprintf("Expected map[string]any, got %T", notification.Result))
}
fmt.Printf("Received notification - Action: %s, Result: %s\n", notification.Action, formatRecordResult(record))
switch notification.Action {
case connection.CreateAction:
fmt.Println("New user created")
case connection.UpdateAction:
fmt.Println("User updated")
case connection.DeleteAction:
fmt.Println("User deleted")
close(received)
}
}
fmt.Println("Notification channel closed")
done <- true
}()
createdUser, err := surrealdb.Create[User](ctx, db, "users", map[string]any{
"username": "alice",
"email": "alice@example.com",
})
if err != nil {
panic(fmt.Sprintf("Failed to create user: %v", err))
}
_, err = surrealdb.Update[User](ctx, db, *createdUser.ID, map[string]any{
"email": "alice.updated@example.com",
})
if err != nil {
panic(fmt.Sprintf("Failed to update user: %v", err))
}
_, err = surrealdb.Delete[User](ctx, db, *createdUser.ID)
if err != nil {
panic(fmt.Sprintf("Failed to delete user: %v", err))
}
select {
case <-received:
case <-time.After(2 * time.Second):
panic("Timeout waiting for all notifications")
}
err = surrealdb.Kill(ctx, db, live.String())
if err != nil {
panic(fmt.Sprintf("Failed to kill live query: %v", err))
}
fmt.Println("Live query terminated")
select {
case <-done:
fmt.Println("Goroutine exited after channel closed")
case <-time.After(2 * time.Second):
panic("Timeout: notification channel was not closed after Kill")
}
}
func ExampleQuery_live() {
config := testenv.MustNewConfig("surrealdbexamples", "livequery_query", "products")
config.Endpoint = testenv.GetSurrealDBWSURL()
db := config.MustNew()
type Product struct {
ID *models.RecordID `json:"id,omitempty"`
Name string `json:"name"`
Price float64 `json:"price"`
Stock int `json:"stock"`
}
ctx := context.Background()
result, err := surrealdb.Query[models.UUID](ctx, db, "LIVE SELECT * FROM products WHERE stock < 10", map[string]any{})
if err != nil {
panic(fmt.Sprintf("Failed to start live query: %v", err))
}
liveID := (*result)[0].Result.String()
fmt.Println("Started live query")
notifications, err := db.LiveNotifications(liveID)
if err != nil {
panic(fmt.Sprintf("Failed to get live notifications channel: %v", err))
}
received := make(chan struct{})
done := make(chan bool)
notificationCount := 0
go func() {
for notification := range notifications {
notificationCount++
record, ok := notification.Result.(map[string]any)
if !ok {
panic(fmt.Sprintf("Expected map[string]any for LIVE SELECT result, got %T", notification.Result))
}
fmt.Printf("Notification %d - Action: %s, Result: %s\n", notificationCount, notification.Action, formatRecordResult(record))
if notificationCount >= 3 {
close(received)
}
}
fmt.Println("Notification channel closed")
done <- true
}()
_, err = surrealdb.Create[Product](ctx, db, "products", map[string]any{
"name": "Widget",
"price": 9.99,
"stock": 5,
})
if err != nil {
panic(fmt.Sprintf("Failed to create product: %v", err))
}
_, err = surrealdb.Create[Product](ctx, db, "products", map[string]any{
"name": "Gadget",
"price": 19.99,
"stock": 3,
})
if err != nil {
panic(fmt.Sprintf("Failed to create second product: %v", err))
}
_, err = surrealdb.Create[Product](ctx, db, "products", map[string]any{
"name": "Abundant Item",
"price": 5.99,
"stock": 100,
})
if err != nil {
panic(fmt.Sprintf("Failed to create third product: %v", err))
}
_, err = surrealdb.Create[Product](ctx, db, "products", map[string]any{
"name": "Rare Item",
"price": 99.99,
"stock": 1,
})
if err != nil {
panic(fmt.Sprintf("Failed to create fourth product: %v", err))
}
select {
case <-received:
case <-time.After(2 * time.Second):
panic("Timeout waiting for all notifications")
}
err = surrealdb.Kill(ctx, db, liveID)
if err != nil {
panic(fmt.Sprintf("Failed to kill live query: %v", err))
}
fmt.Println("Live query terminated")
select {
case <-done:
fmt.Println("Goroutine exited after channel closed")
case <-time.After(2 * time.Second):
panic("Timeout: notification channel was not closed after Kill")
}
}
func ExampleLive_withDiff() {
config := testenv.MustNewConfig("surrealdbexamples", "livequery_diff", "inventory")
config.Endpoint = testenv.GetSurrealDBWSURL()
db := config.MustNew()
type Item struct {
ID *models.RecordID `json:"id,omitempty"`
Name string `json:"name"`
Quantity int `json:"quantity"`
}
ctx := context.Background()
live, err := surrealdb.Live(ctx, db, "inventory", true)
if err != nil {
panic(fmt.Sprintf("Failed to start live query with diff: %v", err))
}
fmt.Println("Started live query with diff enabled")
notifications, err := db.LiveNotifications(live.String())
if err != nil {
panic(fmt.Sprintf("Failed to get live notifications channel: %v", err))
}
received := make(chan struct{})
done := make(chan bool)
go func() {
for notification := range notifications {
var resultStr string
if notification.Action == connection.DeleteAction {
record, ok := notification.Result.(map[string]any)
if !ok {
panic(fmt.Sprintf("Expected map[string]any for DELETE result, got %T", notification.Result))
}
resultStr = formatRecordResult(record)
close(received)
} else {
diffs, ok := notification.Result.([]any)
if !ok {
panic(fmt.Sprintf("Expected []any for diff result, got %T", notification.Result))
}
resultStr = formatDiffResult(diffs)
}
fmt.Printf("Action: %s, Result: %s\n", notification.Action, resultStr)
}
fmt.Println("Notification channel closed")
done <- true
}()
item, err := surrealdb.Create[Item](ctx, db, "inventory", map[string]any{
"name": "Screwdriver",
"quantity": 50,
})
if err != nil {
panic(fmt.Sprintf("Failed to create item: %v", err))
}
_, err = surrealdb.Update[Item](ctx, db, *item.ID, map[string]any{
"quantity": 45,
})
if err != nil {
panic(fmt.Sprintf("Failed to update item: %v", err))
}
_, err = surrealdb.Delete[Item](ctx, db, *item.ID)
if err != nil {
panic(fmt.Sprintf("Failed to delete item: %v", err))
}
select {
case <-received:
case <-time.After(2 * time.Second):
panic("Timeout waiting for all notifications")
}
err = surrealdb.Kill(ctx, db, live.String())
if err != nil {
panic(fmt.Sprintf("Failed to kill live query: %v", err))
}
fmt.Println("Live query with diff terminated")
select {
case <-done:
fmt.Println("Goroutine exited after channel closed")
case <-time.After(2 * time.Second):
panic("Timeout: notification channel was not closed after Kill")
}
}