const DefaultIndexOptions
DefaultIndexOptions defines the default options for new indexes.
Package datastore provides support to store multiple collections of records.
Package provides support to store multiple collections of records.
It supports the definition of multiple storages, where each one is a collection of records. Records can have any number of user defined fields which are added dynamically when values are set on a record. These fields can also be renamed or removed.
Storages have support for simple schemas that allow users to pre-define fields which can optionally have a default value also defined. Default values are assigned to new records on creation.
User defined schemas can optionally be strict, which means that records from a storage using the schema can only assign values to the pre-defined set of fields. In which case, assigning a value to an unknown field results in an error.
Package also supports the definition of custom record indexes. Indexes are used by storages to search and iterate records. The default index is the ID index but custom single and multi value indexes can be defined.
Using this package to store your realm data must be carefully considered. The fact that record fields are not strictly typed and can be renamed or removed could lead to issues if not careful when coding your realm(s). So it's recommended that you consider other alternatives first, like alternative patterns or solutions provided by the blockchain to deal with data, types and data migration for example.
Repository can be found at jeronimoalbi/gnome,
as part of jeronimoalbi's Gno smart contracts monorepo.
1package main
2
3import "gno.land/p/g17khqpukees4237dtn3astzapmp462vjhsz6st4/datastore"
4
5func main() {
6 db := datastore.NewDatastore()
7
8 // Define a unique case insensitive index for user emails
9 emailIdx := datastore.NewIndex("email", func(r datastore.Record) string {
10 return r.MustGet("email").(string)
11 }).Unique().CaseInsensitive()
12
13 // Create a new storage for user records
14 users := db.CreateStorage("users", datastore.WithIndex(emailIdx))
15
16 // Add a user with a single "email" field
17 user := users.NewRecord()
18 user.Set("email", "[email protected]")
19
20 // Save to assign the user ID and update indexes
21 user.Save()
22
23 // Find user by email using the custom index
24 user, _ = users.Get(emailIdx.Name(), "[email protected]")
25 println("Found by email:", user.MustGet("email"))
26
27 // Find user by ID
28 user, _ = users.GetByID(user.ID())
29 println("Found by ID:", user.ID())
30
31 // Delete the user from the storage and update indexes
32 users.Delete(user.ID())
33 println("Users:", users.Size())
34}
35
36// Output:
37// Found by email: [email protected]
38// Found by ID: 1
39// Users: 0
1package main
2
3import "gno.land/p/g17khqpukees4237dtn3astzapmp462vjhsz6st4/datastore"
4
5func main() {
6 db := datastore.NewDatastore()
7
8 // Define a multi value index to search posts by tag
9 tagsIdx := datastore.NewMultiValueIndex("tags", func(r datastore.Record) []string {
10 return r.MustGet("tags").([]string)
11 })
12
13 posts := db.CreateStorage("posts", datastore.WithIndex(tagsIdx))
14 for _, title := range []string{"Post 1", "Post 2", "Post 3"} {
15 post := posts.NewRecord()
16 post.Set("title", title)
17 post.Set("tags", []string{"gno", title})
18 post.Save()
19 }
20
21 // Get two records starting from the second one
22 recordset, err := posts.Query(datastore.WithOffset(1), datastore.WithSize(2))
23 if err != nil {
24 panic(err)
25 }
26
27 recordset.Iterate(func(r datastore.Record) bool {
28 println("Post:", r.MustGet("title"))
29 return false // Continue iterating
30 })
31
32 // Get all the records that have the "gno" tag using the custom index
33 recordset = posts.MustQuery(datastore.UseIndex("tags", "gno"))
34 println("Tagged posts:", recordset.Size())
35}
36
37// Output:
38// Post: Post 2
39// Post: Post 3
40// Tagged posts: 3
Package datastore provides support to store multiple collections of records.
DefaultIndexOptions defines the default options for new indexes.
ErrStorageExists indicates that a storage exists with the same name.
ErrUndefinedField indicates that a field in not defined in a record's schema.
NewDatastore creates a new store.
UseIndex assigns the index that the query must use to get the records. Using an index requires a key value to locate the records within the index.
WithOffset assigns the offset or position of the first record that query must return. The minimum allowed offset is 0.
WithSize assigns the maximum number of records that query can return. The minimum allowed size is 1.
NewSchema creates a new schema.
Strict configures the schema as a strict one. By default schemas should allow the creation of any user defined field, making them strict limits the allowed record fields to the ones pre-defined in the schema. Fields are pre-defined using `WithField`, `WithDefaultField` or by calling `Schema.AddField()`.
WithDefaultField assign a new field with a default value to the schema definition. Default value is assigned to newly created records asociated to to schema.
WithField assign a new field to the schema definition.
NewStorage creates a new records storage.
WithIndex assigns an index to the storage.
WithSchema assigns a schema to the storage.
Datastore is a store that can contain multiple named storages. A storage is a collection of records.
Example usage:
CreateStorage creates a new named storage within the data store.
GetStorage returns a storage that has been created with a specific name. It returns nil when a storage with the specified name is not found.
HasStorage checks if data store contains a storage with a specific name.
Index defines a type for custom user defined storage indexes. Storages are by default indexed by the auto geneated record ID but can additionally be indexed by other custom record fields.
CaseInsensitive returns a copy of the index that indexes record values ignoring casing. Returned index contains previous options plus the case insensitivity one.
Func returns the function that storage collections apply to each record to get the value to use for indexing it.
Name returns index's name.
Options returns current index options. These options define the index behavior regarding case sensitivity and uniquenes.
Unique returns a copy of the index that indexes record values as unique values. Returned index contains previous options plus the unique one.
IndexFn defines a type for single value indexing functions. This type of function extracts a single string value from a record that is then used to index it.
IndexMultiValueFn defines a type for multi value indexing functions. This type of function extracts multiple string values from a record that are then used to index it.
Query contains arguments for querying a storage.
IndexKey return the index key value to locate the records. An empty string is returned when all indexed records match the query.
IndexName returns the name of the storage index being used for the query.
IsEmpty checks if the query is empty. Empty queries return no records.
Offset returns the position of the first record to return. The minimum offset value is 0.
Size returns the maximum number of records a query returns.
QueryOption configures queries.
1type ReadOnlyRecord interface {
2 // ID returns record's ID
3 ID() uint64
4
5 // Key returns a string representation of the record's ID.
6 // It's used to be able to search records within the ID index.
7 Key() string
8
9 // Type returns the record's type.
10 Type() string
11
12 // Fields returns the list of the record's field names.
13 Fields() []string
14
15 // IsEmpty checks if the record has no values.
16 IsEmpty() bool
17
18 // HasField checks if the record has a specific field.
19 HasField(name string) bool
20
21 // Get returns the value of a record's field.
22 Get(field string) (value interface{}, found bool)
23
24 // MustGet returns the value of a record's field or panics when the field is not found.
25 MustGet(field string) interface{}
26}ReadOnlyRecord defines an interface for read-only records.
1type Record interface {
2 ReadOnlyRecord
3
4 // Set assings a value to a record field.
5 // If the field doesn't exist it's created if the underlying schema allows it.
6 // Storage schema can optionally be strict in which case no new fields other than
7 // the ones that were previously defined are allowed.
8 Set(field string, value interface{}) error
9
10 // Save assigns an ID to newly created records and update storage indexes.
11 Save() bool
12}Record stores values for one or more fields.
RecordIterFn defines a type for record iteration functions.
Recordset defines an interface that allows iterating multiple records.
Schema contains information about fields and default field values. It also offers the possibility to configure it as static to indicate that only configured fields should be allowed.
AddField adds a new field to the schema. A default field value can be specified, otherwise `defaultValue` must be nil.
Fields returns the list field names that are defined in the schema.
GetDefault returns the default value for a field.
GetDefaultByIndex returns the default value for a field by it's index.
GetFieldIndex returns the index number of a schema field.
Field index indicates the order the field has within the schema. When defined fields are added they get an index starting from field index 0.
Fields are internally referenced by index number instead of the name to be able to rename fields easily.
GetFieldName returns the name of a field for a specific field index.
HasField check is a field has been defined in the schema.
IsStrict check if the schema is configured as a strict one.
Name returns schema's name.
RenameField renames a field.
Size returns the number of fields the schema has.
StorageOption configures schemas.
Storage stores a collection of records.
By default it searches records by record ID but it allows using custom user defined indexes for other record fields.
When a storage is created it defines a default schema that keeps track of record fields. Storage can be optionally created with a user defined schema in cases where the number of fields has to be pre-defined or when new records must have one or more fields initialized to default values.
Collection returns the undelying collection used by the storage to store all records.
Delete deletes a record from the storage.
Get returns the first record found for a key within a storage index.
This is a convenience method to get a single record. A multi index will always return the first record value for the specified key in this case. To get multiple records create a query using a custom index and key value or use the underlying storage collection.
GetByID returns a record whose ID matches the specified ID.
MustQuery returns a recordset that matches the query parameters or panics on error. By default query selects records using the ID index.
Example usage:
Name returns storage's name.
NewRecord creates a new storage record.
If a custom schema with default field values is assigned to storage it's used to assign initial default values when new records are created.
Creating a new record doesn't assign an ID to it, a new ID is generated and assigned to the record when it's saved for the first time.
Query returns a recordset that matches the query parameters. By default query selects records using the ID index.
Example usage:
Schema returns the schema being used to track record fields.
Size returns the number of records that the storage have.
StorageOption configures storages.