Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

133 Commits

Repository files navigation

Storm

Build StatusGoDocGo Report CardCoverage

Storm is a 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.

Getting Started

go get -u github.com/asdine/storm

Import Storm

import"github.com/asdine/storm"

Open a database

Quick way of opening a database

db, err:=storm.Open("my.db")
deferdb.Close()

Open can receive multiple options to customize the way it behaves. See Options below

Simple ORM

Declare your structures

typeUserstruct {
IDint// primary keyGroupstring`storm:"index"`// this field will be indexedEmailstring`storm:"unique"`// this field will be indexed with a unique constraintNamestring// this field will not be indexedAgeint`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.

typeUserstruct {
ThePrimaryKeystring`storm:"id"`// primary keyGroupstring`storm:"index"`// this field will be indexedEmailstring`storm:"unique"`// this field will be indexed with a unique constraintNamestring// this field will not be indexed
}

Storm handles tags in nested structures with the inline tag

typeBasestruct {
Ident bson.ObjectId`storm:"id"`
}
typeUserstruct {
Base`storm:"inline"`Groupstring`storm:"index"`Emailstring`storm:"unique"`NamestringCreatedAt time.Time`storm:"index"`
}

Save your object

user:=User{
ID: 10,
Group: "staff",
Email: "john@provider.com",
Name: "John",
Age: 21,
CreatedAt: time.Now(),
}
err:=db.Save(&user)
// err == niluser.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.

Fetch your object

Only indexed fields can be used to find a record

varuserUsererr:=db.One("Email", "john@provider.com", &user)
// err == nilerr=db.One("Name", "John", &user)
// err == "not found"

Fetch multiple objects

varusers []Usererr:=db.Find("Group", "staff", &users)

Fetch all objects

varusers []Usererr:=db.All(&users)

Fetch all objects sorted by index

varusers []Usererr:=db.AllByIndex("CreatedAt", &users)

Fetch a range of objects

varusers []Usererr:=db.Range("Age", 10, 21, &users)

Skip and Limit

varusers []Usererr:=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))

Remove an object

err:=db.Remove(&user)

Initialize buckets and indexes before saving an object

err:=db.Init(&User{})

Useful when starting your application

Drop a bucket

err:=db.Drop("User")

Transactions

tx, err:=db.Begin(true)
accountA.Amount-=100accountB.Amount+=100err=tx.Save(accountA)
iferr!=nil {
tx.Rollback()
returnerr
}
err=tx.Save(accountB)
iferr!=nil {
tx.Rollback()
returnerr
}
tx.Commit()

Options

Storm options are functions that can be passed when constructing you Storm instance. You can pass it any number of options.

BoltOptions

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}))

EncodeDecoder

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))
Provided Codecs

You can easily implement your own EncodeDecoder, but Storm comes with built-in support for GOB (default), JSON, Sereal and Protocol Buffers

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""github.com/asdine/storm/codec/protobuf"
)
vargobDb, _=storm.Open("gob.db", storm.Codec(gob.Codec))
varjsonDb, _=storm.Open("json.db", storm.Codec(json.Codec))
varserealDb, _=storm.Open("sereal.db", storm.Codec(sereal.Codec))
varprotobufDb, _=storm.Open("protobuf.db", storm.Codec(protobuf.Codec))

Auto Increment

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())

Use existing Bolt connection

You can use an existing connection and pass it to Storm

bDB, _:=bolt.Open(filepath.Join(dir, "bolt.db"), 0600, &bolt.Options{Timeout: 10*time.Second})
db:=storm.Open("", storm.UseDB(bDB))

Nodes and nested buckets

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"))
varissues []Issueerr=repo.Find("Author", user.ID, &issues)
varreleaseReleaseerr=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")

Simple Key/Value store

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)
vardetailsmap[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

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))
returnnil
})

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)
...returnnil
})

License

MIT

Author

Asdine El Hrychy

About

Simple and powerful ORM for BoltDB

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages