record.gno
5.38 Kb ยท 229 lines
1package datastore
2
3import (
4 "errors"
5
6 "gno.land/p/demo/avl"
7 "gno.land/p/demo/seqid"
8 "gno.land/p/moul/collection"
9)
10
11// ErrUndefinedField indicates that a field in not defined in a record's schema.
12var ErrUndefinedField = errors.New("undefined field")
13
14type (
15 // Record stores values for one or more fields.
16 Record interface {
17 ReadOnlyRecord
18
19 // Set assings a value to a record field.
20 // If the field doesn't exist it's created if the underlying schema allows it.
21 // Storage schema can optionally be strict in which case no new fields other than
22 // the ones that were previously defined are allowed.
23 Set(field string, value interface{}) error
24
25 // Save assigns an ID to newly created records and update storage indexes.
26 Save() bool
27 }
28
29 // ReadOnlyRecord defines an interface for read-only records.
30 ReadOnlyRecord interface {
31 // ID returns record's ID
32 ID() uint64
33
34 // Key returns a string representation of the record's ID.
35 // It's used to be able to search records within the ID index.
36 Key() string
37
38 // Type returns the record's type.
39 Type() string
40
41 // Fields returns the list of the record's field names.
42 Fields() []string
43
44 // IsEmpty checks if the record has no values.
45 IsEmpty() bool
46
47 // HasField checks if the record has a specific field.
48 HasField(name string) bool
49
50 // Get returns the value of a record's field.
51 Get(field string) (value interface{}, found bool)
52
53 // MustGet returns the value of a record's field or panics when the field is not found.
54 MustGet(field string) interface{}
55 }
56
57 // RecordIterFn defines a type for record iteration functions.
58 RecordIterFn func(Record) (stop bool)
59
60 // Recordset defines an interface that allows iterating multiple records.
61 Recordset interface {
62 // Iterate iterates records in order.
63 Iterate(fn RecordIterFn) (stopped bool)
64
65 // ReverseIterate iterates records in reverse order.
66 ReverseIterate(fn RecordIterFn) (stopped bool)
67
68 // Size returns the number of records in the recordset.
69 Size() int
70 }
71)
72
73type record struct {
74 id uint64
75 schema *Schema
76 collection *collection.Collection
77 values avl.Tree // string(field index) -> interface{}
78}
79
80// ID returns record's ID
81func (r record) ID() uint64 {
82 return r.id
83}
84
85// Key returns a string representation of the record's ID.
86// It's used to be able to search records within the ID index.
87func (r record) Key() string {
88 return seqid.ID(r.id).String()
89}
90
91// Type returns the record's type.
92func (r record) Type() string {
93 return r.schema.Name()
94}
95
96// Fields returns the list of the record's field names.
97func (r record) Fields() []string {
98 return r.schema.Fields()
99}
100
101// IsEmpty checks if the record has no values.
102func (r record) IsEmpty() bool {
103 return r.values.Size() == 0
104}
105
106// HasField checks if the record has a specific field.
107func (r record) HasField(name string) bool {
108 return r.schema.HasField(name)
109}
110
111// Set assings a value to a record field.
112// If the field doesn't exist it's created if the underlying schema allows it.
113// Storage schema can optionally be strict in which case no new fields other than
114// the ones that were previously defined are allowed.
115func (r *record) Set(field string, value interface{}) error {
116 i := r.schema.GetFieldIndex(field)
117 if i == -1 {
118 if r.schema.IsStrict() {
119 return ErrUndefinedField
120 }
121
122 i, _ = r.schema.AddField(field, nil)
123 }
124
125 key := castIntToKey(i)
126 r.values.Set(key, value)
127 return nil
128}
129
130// Get returns the value of a record's field.
131func (r record) Get(field string) (value interface{}, found bool) {
132 i := r.schema.GetFieldIndex(field)
133 if i == -1 {
134 return nil, false
135 }
136
137 key := castIntToKey(i)
138 return r.values.Get(key)
139}
140
141// MustGet returns the value of a record's field or panics when the field is not found.
142func (r record) MustGet(field string) interface{} {
143 v, found := r.Get(field)
144 if !found {
145 panic("field not found: " + field)
146 }
147 return v
148}
149
150// Save assigns an ID to newly created records and update storage indexes.
151func (r *record) Save() bool {
152 if r.id == 0 {
153 r.id = r.collection.Set(r)
154 return r.id != 0
155 }
156 return r.collection.Update(r.id, r)
157}
158
159type recordset struct {
160 query Query
161 records avl.ITree
162 keys []string
163 size int
164}
165
166// Iterate iterates records in order.
167func (rs recordset) Iterate(fn RecordIterFn) (stopped bool) {
168 if rs.isUsingCustomIndex() {
169 for _, k := range rs.keys {
170 v, found := rs.records.Get(k)
171 if !found {
172 continue
173 }
174
175 if fn(v.(Record)) {
176 return true
177 }
178 }
179
180 return false
181 }
182
183 offset := rs.query.Offset()
184 count := rs.query.Size()
185 if count == 0 {
186 count = rs.records.Size()
187 }
188
189 return rs.records.IterateByOffset(offset, count, func(_ string, v interface{}) bool {
190 return fn(v.(Record))
191 })
192}
193
194// ReverseIterate iterates records in reverse order.
195func (rs recordset) ReverseIterate(fn RecordIterFn) (stopped bool) {
196 if rs.isUsingCustomIndex() {
197 for i := len(rs.keys) - 1; i >= 0; i-- {
198 v, found := rs.records.Get(rs.keys[i])
199 if !found {
200 continue
201 }
202
203 if fn(v.(Record)) {
204 return true
205 }
206 }
207
208 return false
209 }
210
211 offset := rs.query.Offset()
212 count := rs.query.Size()
213 if count == 0 {
214 count = rs.records.Size()
215 }
216
217 return rs.records.ReverseIterateByOffset(offset, count, func(_ string, v interface{}) bool {
218 return fn(v.(Record))
219 })
220}
221
222// Size returns the number of records in the recordset.
223func (rs recordset) Size() int {
224 return rs.size
225}
226
227func (rs recordset) isUsingCustomIndex() bool {
228 return rs.query.IndexName() != collection.IDIndex
229}