Storm is simple and powerful ORM for BoltDB. The goal of this project is to provide a simple way to save any object in BoltDB and to easily retrieve it.
go get -u github.com/asdine/storm
import "github.com/asdine/storm"
Quick way of opening a database
db, err := storm.Open("my.db")
defer db.Close()
Open
can receive multiple options to customize the way it behaves. See Options below
type User struct {
ID int // primary key
Group string `storm:"index"` // this field will be indexed
Email string `storm:"unique"` // this field will be indexed with a unique constraint
Name string // this field will not be indexed
Age int `storm:"index"`
}
The primary key can be of any type as long as it is not a zero value. Storm will search for the tag id
, if not present Storm will search for a field named ID
.
type User struct {
ThePrimaryKey string `storm:"id"`// primary key
Group string `storm:"index"` // this field will be indexed
Email string `storm:"unique"` // this field will be indexed with a unique constraint
Name string // this field will not be indexed
}
Storm handles tags in nested structures with the inline
tag
type Base struct {
Ident bson.ObjectId `storm:"id"`
}
type User struct {
Base `storm:"inline"`
Group string `storm:"index"`
Email string `storm:"unique"`
Name string
CreatedAt time.Time `storm:"index"`
}
user := User{
ID: 10,
Group: "staff",
Email: "john@provider.com",
Name: "John",
Age: 21,
CreatedAt: time.Now(),
}
err := db.Save(&user)
// err == nil
user.ID++
err = db.Save(&user)
// err == "already exists"
That's it.
Save
creates or updates all the required indexes and buckets, checks the unique constraints and saves the object to the store.
Only indexed fields can be used to find a record
var user User
err := db.One("Email", "john@provider.com", &user)
// err == nil
err = db.One("Name", "John", &user)
// err == "not found"
var users []User
err := db.Find("Group", "staff", &users)
var users []User
err := db.All(&users)
var users []User
err := db.AllByIndex("CreatedAt", &users)
var users []User
err := db.Range("Age", 10, 21, &users)
var users []User
err := db.Find("Group", "staff", &users, storm.Skip(10))
err = db.Find("Group", "staff", &users, storm.Limit(10))
err = db.Find("Group", "staff", &users, storm.Limit(10), storm.Skip(10))
err = db.All(&users, storm.Limit(10), storm.Skip(10))
err = db.AllByIndex("CreatedAt", &users, storm.Limit(10), storm.Skip(10))
err = db.Range("Age", 10, 21, &users, storm.Limit(10), storm.Skip(10))
err := db.Remove(&user)
err := db.Init(&User{})
Useful when starting your application
tx, err := db.Begin(true)
accountA.Amount -= 100
accountB.Amount += 100
err = tx.Save(accountA)
if err != nil {
tx.Rollback()
return err
}
err = tx.Save(accountB)
if err != nil {
tx.Rollback()
return err
}
tx.Commit()
Storm options are functions that can be passed when constructing you Storm instance. You can pass it any number of options.
By default, Storm opens a database with the mode 0600
and a timeout of one second.
You can change this behavior by using BoltOptions
db, err := storm.Open("my.db", storm.BoltOptions(0600, &bolt.Options{Timeout: 1 * time.Second}))
To store the data in BoltDB, Storm encodes it in GOB by default. If you wish to change this behavior you can pass a codec that implements codec.EncodeDecoder
via the storm.Codec
option:
db := storm.Open("my.db", storm.Codec(myCodec))
You can easily implement your own EncodeDecoder
, but Storm comes with built-in support for GOB (default), JSON, and Sereal.
These can be used by importing the relevant package and use that codec to configure Storm. The example below shows all three (without proper error handling):
import (
"github.com/asdine/storm"
"github.com/asdine/storm/codec/gob"
"github.com/asdine/storm/codec/json"
"github.com/asdine/storm/codec/sereal"
)
var gobDb, _ = storm.Open("gob.db", storm.Codec(gob.Codec))
var jsonDb, _ = storm.Open("json.db", storm.Codec(json.Codec))
var serealDb, _ = storm.Open("sereal.db", storm.Codec(sereal.Codec))
Storm can auto increment integer IDs so you don't have to worry about that when saving your objects.
db := storm.Open("my.db", storm.AutoIncrement())
Storm takes advantage of BoltDB nested buckets feature by using storm.Node
.
A storm.Node
is the underlying object used by storm.DB
to manipulate a bucket.
To create a nested bucket and use the same API as storm.DB
, you can use the DB.From
method.
repo := db.From("repo")
err := repo.Save(&Issue{
Title: "I want more features",
Author: user.ID,
})
err = repo.Save(newRelease("0.10"))
var issues []Issue
err = repo.Find("Author", user.ID, &issues)
var release Release
err = repo.One("Tag", "0.10", &release)
You can also chain the nodes to create a hierarchy
chars := db.From("characters")
heroes := chars.From("heroes")
enemies := chars.From("enemies")
items := db.From("items")
potions := items.From("consumables").From("medicine").From("potions")
You can even pass the entire hierarchy as arguments to From
:
privateNotes := db.From("notes", "private")
workNotes := db.From("notes", "work")
Storm can be used as a simple, robust, key/value store that can store anything. The key and the value can be of any type as long as the key is not a zero value.
Saving data :
db.Set("logs", time.Now(), "I'm eating my breakfast man")
db.Set("sessions", bson.NewObjectId(), &someUser)
db.Set("weird storage", "754-3010", map[string]interface{}{
"hair": "blonde",
"likes": []string{"cheese", "star wars"},
})
Fetching data :
user := User{}
db.Get("sessions", someObjectId, &user)
var details map[string]interface{}
db.Get("weird storage", "754-3010", &details)
db.Get("sessions", someObjectId, &details)
Deleting data :
db.Delete("sessions", someObjectId)
db.Delete("weird storage", "754-3010")
BoltDB is still easily accessible and can be used as usual
db.Bolt.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("my bucket"))
val := bucket.Get([]byte("any id"))
fmt.Println(string(val))
return nil
})
A transaction can be also be passed to Storm
db.Bolt.Update(func(tx *bolt.Tx) error {
...
dbx := db.WithTransaction(tx)
err = dbx.Save(&user)
...
return nil
})
- Search
- Reverse order
- More indexes
MIT
Asdine El Hrychy