-
Notifications
You must be signed in to change notification settings - Fork 85
Expand file tree
/
Copy pathexample_query_changefeed_test.go
More file actions
152 lines (136 loc) · 4.54 KB
/
Copy pathexample_query_changefeed_test.go
File metadata and controls
152 lines (136 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package surrealdb_test
import (
"context"
"fmt"
"github.com/surrealdb/surrealdb.go"
"github.com/surrealdb/surrealdb.go/contrib/testenv"
)
// ExampleQuery_changeFeedSchemaless demonstrates how to use Change Feeds in SurrealDB
// to track changes made to database records.
//
//nolint:gocyclo
func ExampleQuery_changeFeedSchemaless() {
// Connect to database
db := testenv.MustNew("surrealdbexamples", "changefeed", "product")
ctx := context.Background()
// Enable change feed on the database
_, err := surrealdb.Query[any](ctx, db, "DEFINE TABLE product CHANGEFEED 1h", nil)
if err != nil {
panic(err)
}
// Make some changes to generate change feed entries
_, err = surrealdb.Query[any](ctx, db, `
CREATE product:1 SET name = "Laptop", price = 999.99;
UPDATE product:1 SET price = 899.99;
CREATE product:2 SET name = "Mouse", price = 29.99;
DELETE product:2;
`, nil)
if err != nil {
panic(err)
}
type ChangeDefineTable struct {
Name string `json:"name"`
}
// Change represents a change to a table in the database.
type Change struct {
// DefineTable represents the definition of a new table in the database.
// It has Name and nothing else.
DefineTable *ChangeDefineTable `json:"define_table"`
// Update represents an update to a table in the database.
// Note that this may represent a new record being created.
// In case of an update, the "id" field must be present,
// and all other fields including unchanged fields must be included.
Update map[string]any `json:"update"`
// Delete represents a deletion from a table in the database.
Delete map[string]any `json:"delete"`
}
// ChangeSet represents a set of changes in the database.
type ChangeSet struct {
// Versionstamp is a unique identifier for the change set.
// It is unique per database.
Versionstamp uint64 `json:"versionstamp"`
// Changes is a list of changes made in the database.
// It may contain one or more table changes,
// each represented as a map of field names to their new values.
Changes []Change `json:"changes"`
}
result, err := surrealdb.Query[[]ChangeSet](ctx, db, "SHOW CHANGES FOR TABLE product SINCE 0", nil)
if err != nil {
panic(err)
}
showChangesResult := (*result)[0].Result
// Verify versionstamps are monotonic
monotonic := true
for i := 1; i < len(showChangesResult); i++ {
if showChangesResult[i].Versionstamp <= showChangesResult[i-1].Versionstamp {
monotonic = false
break
}
}
fmt.Printf("Versionstamps are monotonic: %v\n", monotonic)
// Count different types of changes
var defineCount, updateCount, deleteCount int
for _, changeSet := range showChangesResult {
for _, change := range changeSet.Changes {
if change.DefineTable != nil {
defineCount++
}
if change.Update != nil {
updateCount++
}
if change.Delete != nil {
deleteCount++
}
}
}
if defineCount > 0 && updateCount > 0 && deleteCount > 0 {
fmt.Println("Found change entries: defines, updates, and deletes")
}
// Show the pattern of the last few changes with actual data
if len(showChangesResult) >= 5 {
lastFive := showChangesResult[len(showChangesResult)-5:]
fmt.Println("Last 5 changes:")
for _, changeSet := range lastFive {
for _, change := range changeSet.Changes {
if change.DefineTable != nil {
fmt.Printf(" DefineTable: %s\n", change.DefineTable.Name)
}
if change.Update != nil {
// Extract key fields from the update
if id, ok := change.Update["id"]; ok {
if name, hasName := change.Update["name"]; hasName {
if price, hasPrice := change.Update["price"]; hasPrice {
fmt.Printf(" Update: id=%v, name=%v, price=%v\n", id, name, price)
} else {
fmt.Printf(" Update: id=%v, name=%v\n", id, name)
}
} else if price, hasPrice := change.Update["price"]; hasPrice {
fmt.Printf(" Update: id=%v, price=%v\n", id, price)
} else {
fmt.Printf(" Update: id=%v\n", id)
}
} else {
fmt.Printf(" Update: %v\n", change.Update)
}
}
if change.Delete != nil {
// Extract id from the delete
if id, ok := change.Delete["id"]; ok {
fmt.Printf(" Delete: id=%v\n", id)
} else {
fmt.Printf(" Delete: %v\n", change.Delete)
}
}
}
}
}
// Output:
// Versionstamps are monotonic: true
// Found change entries: defines, updates, and deletes
// Last 5 changes:
// DefineTable: product
// Update: id={product 1}, name=Laptop, price=999.99
// Update: id={product 1}, name=Laptop, price=899.99
// Update: id={product 2}, name=Mouse, price=29.99
// Delete: id={product 2}
}