Documentation ¶
Overview ¶
Package ds (short for "data store") is a key-value store with hash indexes. It allows for rudimentary but lightning fast retrieval of grouped or relevant data without having to iterate over all objects in the store.
Define the primary key, indexed keys, and unique keys as tags on struct fields, and DS takes care of the rest.
Index ¶
- type Config
- type Field
- type GetOptions
- type MigrateParams
- type MigrationResults
- type Options
- type Table
- func (table *Table) Add(o interface{}) error
- func (table *Table) Close()
- func (table *Table) Delete(o interface{}) error
- func (table *Table) DeleteAll() error
- func (table *Table) DeleteAllIndex(fieldName string, value interface{}) error
- func (table *Table) DeletePrimaryKey(o interface{}) error
- func (table *Table) DeleteUnique(field string, o interface{}) error
- func (table *Table) Get(primaryKey interface{}) (interface{}, error)
- func (table *Table) GetAll(options *GetOptions) ([]interface{}, error)
- func (table *Table) GetIndex(fieldName string, value interface{}, options *GetOptions) ([]interface{}, error)
- func (table *Table) GetUnique(fieldName string, value interface{}) (interface{}, error)
- func (table *Table) IsIndexed(field string) bool
- func (table *Table) IsUnique(field string) bool
- func (table *Table) Update(o interface{}) error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct { Fields []Field TypeOf string PrimaryKey string Indexes []string Uniques []string LastInsertIndex uint64 Version int }
Config describes ds table configuration
type GetOptions ¶
type GetOptions struct { // Should the results be sorted. Does nothing for unsorted tables Sorted bool // If results are to be sorted, should they be from most recent to oldest (true) or invese (false) Ascending bool // The maximum number of entries to return. 0 means unlimited Max int }
GetOptions describes options for getting entries from a DS table
type MigrateParams ¶
type MigrateParams struct { // TablePath the path to the existing table file TablePath string // NewPath the path for the new table file. This can be the same as the old table. NewPath string // OldType an instance of a struct object that has the same definition as the existing table. OldType interface{} // NewType an instance of a struct object that has the definition that shall be used. This can be the same as the // OldType. NewType interface{} // DisableSorting (optional) if the current table is sorted, set this to true to disable sorting // Note: This is irreversible! DisableSorting bool // MigrateObject method called for each entry in the table in reverse order. Return a new type, error, or nil. // Migration is halted if an error is returned. // Return (nil, nil) and the entry will be skipped from migration, but migration will continue. MigrateObject func(o interface{}) (interface{}, error) // KeepBackup (optional) if false the backup copy of the table will be discarded if the migration was successful. If true // the copy is not deleted. KeepBackup bool }
MigrateParams describes the parameters to perform a DS table migration. All fields are required unless otherwise specified.
type MigrationResults ¶
type MigrationResults struct { // Success was the migration successful Success bool // Error if unsuccessful, this will be the error that caused the failure Error error // EntriesMigrated the number of entries migrated EntriesMigrated uint // EntriesSkipped the number of entries skipped EntriesSkipped uint }
MigrationResults describes results from a migration
func Migrate ¶
func Migrate(params MigrateParams) (results MigrationResults)
Migrate will migrate a DS table from one object type to another. You must migrate if the old data type is not compatible with the new type, such as if an existing field was changed. You don't need to migrate if you add or remove an existing field.
Before the existing data is touched, a copy is made with "_backup" appended to the filename, and a new table file is created with the migrated entries. Upon successful migration, the backup copy is deleted (by default). If the table being migrated is sorted, the original order is preserved.
Ensure you read the documentation of the MigrateParams struct, as it goes into greater detail on the parameters required for migration, and what they do.
Example ¶
package main import ( "fmt" "github.com/ecnepsnai/ds" ) func main() { // Define a struct that maps to the current type used in the table type oldType struct { Username string `ds:"primary"` Email string `ds:"unique"` FirstName string LastName string } // Define your new struct type newType struct { Username string `ds:"primary"` Email string `ds:"unique"` Name string } // In this example, we're merging the "FirstName" and "LastName" fields of the User object to // just a single "Name" field result := ds.Migrate(ds.MigrateParams{ TablePath: "/path/to/table.db", NewPath: "/path/to/table.db", // You can specify the same path, or a new one if you want OldType: oldType{}, NewType: newType{}, // NewType can be the same as the old type if you aren't changing the struct MigrateObject: func(o interface{}) (interface{}, error) { old := o.(oldType) // Within the MigrateObject function you can: // 1. Return a object of the NewType (specified in the MigrateParams) // 2. Return an error and the migration will abort // 3. Return nil and this entry will be skipped return newType{ Username: old.Username, Email: old.Email, Name: old.FirstName + " " + old.LastName, }, nil }, }) if !result.Success { // Migration failed. panic(result.Error) } fmt.Printf("Migration successful. Entries migrated: %d, skipped: %d\n", result.EntriesMigrated, result.EntriesSkipped) }
Output:
type Options ¶
type Options struct { // DisableSorting disable all sorting features. This will make tables smaller, and inserts/removes/deletes faster. DisableSorting bool // contains filtered or unexported fields }
Options describes options for DS tables. Once set, these cannot be changed.
type Table ¶
type Table struct { Name string // contains filtered or unexported fields }
Table describes a ds table. A table is mapped to a single registered object type and contains both the data and the indexes.
func Register ¶
Register will register an instance of a struct with ds, creating a table (or opening an existing table) for this type at the specified file path.
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { type User struct { // Primary fields represent the primary key of the object. Your object must have exactly one primary field // and its value is unique Username string `ds:"primary"` // Unique fields function just like primary fields except any field (other than the primary field) can be unique Email string `ds:"unique"` // Index fields represent fields where objects with identical values are grouped together so they can be fetched // quickly later Enabled bool `ds:"index"` // Fields with no ds tag are saved, but you can't fetch based on their value, and can have duplicate values // between entries Password string } tablePath := "user.db" table, err := ds.Register(User{}, tablePath, nil) if err != nil { panic(err) } // Don't forget to close your table when you're finished table.Close() }
Output:
func (*Table) Add ¶
Add will add a new object to the table. o must the the same type that was used to register the table and cannot be a pointer.
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { type User struct { Username string `ds:"primary"` Password string Email string `ds:"unique"` Enabled bool `ds:"index"` } var table *ds.Table // Assumes the table is already registered, see ds.Register for an example newUser := User{ Username: "ian", Password: "hunter2", Email: "email@domain", Enabled: true, } if err := table.Add(newUser); err != nil { panic(err) } }
Output:
func (*Table) Close ¶
func (table *Table) Close()
Close will close the table. This will not panic if the table has not been opened or already been closed.
func (*Table) Delete ¶
Delete will delete the provided object and clean indexes
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { type User struct { Username string `ds:"primary"` Password string Email string `ds:"unique"` Enabled bool `ds:"index"` } var table *ds.Table // Assumes the table is already registered, see ds.Register for an example deleteUser := User{ Username: "ian", Password: "hunter2", Email: "email@domain", Enabled: true, } // Delete the object if err := table.Delete(deleteUser); err != nil { panic(err) } }
Output:
func (*Table) DeleteAll ¶
DeleteAll delete all objects from the table
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { var table *ds.Table // Assumes the table is already registered, see ds.Register for an example // Delete all objects if err := table.DeleteAll(); err != nil { panic(err) } }
Output:
func (*Table) DeleteAllIndex ¶
DeleteAllIndex will delete all objects matching the given indexed fields value
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { var table *ds.Table // Assumes the table is already registered, see ds.Register for an example // Delete all objects with the following indexed fields value if err := table.DeleteAllIndex("Enabled", false); err != nil { panic(err) } }
Output:
func (*Table) DeletePrimaryKey ¶
DeletePrimaryKey will delete the object with the associated primary key and clean indexes. Does nothing if not object matches the given primary key.
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { var table *ds.Table // Assumes the table is already registered, see ds.Register for an example // Delete an object by its primary key if err := table.DeletePrimaryKey("ian"); err != nil { panic(err) } }
Output:
func (*Table) DeleteUnique ¶
DeleteUnique will delete the object with the associated unique value and clean indexes. Does nothing if no object matched the given unique fields value.
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { var table *ds.Table // Assumes the table is already registered, see ds.Register for an example // Delete an object by a unique fields value if err := table.DeleteUnique("Email", "user@domain"); err != nil { panic(err) } }
Output:
func (*Table) Get ¶
Get will get a single entry by its primary key. Returns (nil, nil) if nothing found.
Example ¶
package main import ( "fmt" "github.com/ecnepsnai/ds" ) func main() { var table *ds.Table // Assumes the table is already registered, see ds.Register for an example type User struct { Username string `ds:"primary"` } object, err := table.Get("ian") if err != nil { panic(err) } if object == nil { // No object with that primary key found } user, ok := object.(User) if !ok { // The object wasn't a `User` } fmt.Printf("Username: %s\n", user) }
Output:
func (*Table) GetAll ¶
func (table *Table) GetAll(options *GetOptions) ([]interface{}, error)
GetAll will get all of the entries in the table.
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { var table *ds.Table // Assumes the table is already registered, see ds.Register for an example type User struct { Username string `ds:"primary"` } // Get only the first 100 users sorted by when they were added objects, err := table.GetAll(&ds.GetOptions{ Sorted: true, Ascending: true, Max: 100, }) if err != nil { panic(err) } if objects == nil { // No objects were returned return } users := make([]User, len(objects)) for i, object := range objects { user, ok := object.(User) if !ok { // The object wasn't a `User` } users[i] = user } }
Output:
func (*Table) GetIndex ¶
func (table *Table) GetIndex(fieldName string, value interface{}, options *GetOptions) ([]interface{}, error)
GetIndex will get multiple entries that contain the same value for the specified indexed field. Result is not ordered. Use GetIndexSorted to return a sorted slice. Returns an empty array if nothing found.
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { var table *ds.Table // Assumes the table is already registered, see ds.Register for an example type User struct { Username string `ds:"primary"` Enabled bool `ds:"index"` } // Get all enabled users objects, err := table.GetIndex("Enabled", true, nil) if err != nil { panic(err) } if objects == nil { // No objects were returned return } users := make([]User, len(objects)) for i, object := range objects { user, ok := object.(User) if !ok { // The object wasn't a `User` } users[i] = user } }
Output:
func (*Table) GetUnique ¶
GetUnique will get a single entry based on the value of the provided unique field. Returns (nil, nil) if nothing found.
Example ¶
package main import ( "fmt" "github.com/ecnepsnai/ds" ) func main() { var table *ds.Table // Assumes the table is already registered, see ds.Register for an example type User struct { Username string `ds:"primary"` Email string `ds:"unique"` } // Get the user with the email user@domain object, err := table.GetUnique("Email", "user@domain") if err != nil { panic(err) } if object == nil { // No object with that primary key found } user, ok := object.(User) if !ok { // The object wasn't a `User` } fmt.Printf("Username: %s\n", user) }
Output:
func (*Table) IsIndexed ¶
IsIndexed is the given field indexed
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { type User struct { Username string `ds:"primary"` Email string `ds:"email"` Enabled bool `ds:"index"` } tablePath := "user.db" table, err := ds.Register(User{}, tablePath, nil) if err != nil { panic(err) } table.IsIndexed("Username") // returns False table.IsIndexed("Enabled") // returns True }
Output:
func (*Table) IsUnique ¶
IsUnique is the given field unique
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { type User struct { Username string `ds:"primary"` Email string `ds:"email"` Enabled bool `ds:"index"` } tablePath := "user.db" table, err := ds.Register(User{}, tablePath, nil) if err != nil { panic(err) } table.IsUnique("Username") // returns False table.IsUnique("Email") // returns True }
Output:
func (*Table) Update ¶
Update will update an existing object in the table. The primary key must match for this object otherwise it will just be inserted as a new object. Updated objects do not change positions in a sorted table.
Example ¶
package main import ( "github.com/ecnepsnai/ds" ) func main() { type User struct { Username string `ds:"primary"` Password string Email string `ds:"unique"` Enabled bool `ds:"index"` } var table *ds.Table // Assumes the table is already registered, see ds.Register for an example newUser := User{ Username: "ian", Password: "hunter2", Email: "email@domain", Enabled: true, } if err := table.Add(newUser); err != nil { panic(err) } newUser.Password = "something else" // Update an existing entry (based on the primary key) if err := table.Update(newUser); err != nil { panic(err) } }
Output: