boltron

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 28, 2018 License: MIT Imports: 6 Imported by: 0

README

GoDoc Go Report Card

boltron

too flexible and rather low level secondary indexes for boltdb

  • the actively maintained (by CoreOS) fork of boltdb is used.
  • tests copied from boltdb - the only part that changed in tests is the import path.

Tests performed using:

$ go test -test.short 
todo
  • add more boltron specific tests
  • add examples

Documentation

Index

Examples

Constants

View Source
const (
	// MaxKeySize is the maximum length of a key, in bytes.
	MaxKeySize = bolt.MaxKeySize

	// MaxValueSize is the maximum length of a value, in bytes.
	MaxValueSize = bolt.MaxValueSize
)
View Source
const (
	Sep = ":"
)

constants

Variables

View Source
var (
	ErrModifyIndex   error = sentinelErr("modifying indexes are not allowed")
	ErrIndexNotFound error = sentinelErr("index not found")
)

errors

View Source
var (
	// ErrDatabaseNotOpen is returned when a DB instance is accessed before it
	// is opened or after it is closed.
	ErrDatabaseNotOpen = bolt.ErrDatabaseNotOpen

	// ErrDatabaseOpen is returned when opening a database that is
	// already open.
	ErrDatabaseOpen = bolt.ErrDatabaseOpen

	// ErrInvalid is returned when both meta pages on a database are invalid.
	// This typically occurs when a file is not a bolt database.
	ErrInvalid = bolt.ErrInvalid

	// ErrVersionMismatch is returned when the data file was created with a
	// different version of Bolt.
	ErrVersionMismatch = bolt.ErrVersionMismatch

	// ErrChecksum is returned when either meta page checksum does not match.
	ErrChecksum = bolt.ErrChecksum

	// ErrTimeout is returned when a database cannot obtain an exclusive lock
	// on the data file after the timeout passed to Open().
	ErrTimeout = bolt.ErrTimeout
)

These errors can be returned when opening or calling methods on a DB.

View Source
var (
	// ErrTxNotWritable is returned when performing a write operation on a
	// read-only transaction.
	ErrTxNotWritable = bolt.ErrTxNotWritable

	// ErrTxClosed is returned when committing or rolling back a transaction
	// that has already been committed or rolled back.
	ErrTxClosed = bolt.ErrTxClosed

	// ErrDatabaseReadOnly is returned when a mutating transaction is started on a
	// read-only database.
	ErrDatabaseReadOnly = bolt.ErrDatabaseReadOnly
)

These errors can occur when beginning or committing a Tx.

View Source
var (
	// ErrBucketNotFound is returned when trying to access a bucket that has
	// not been created yet.
	ErrBucketNotFound = bolt.ErrBucketNotFound

	// ErrBucketExists is returned when creating a bucket that already exists.
	ErrBucketExists = bolt.ErrBucketExists

	// ErrBucketNameRequired is returned when creating a bucket with a blank name.
	ErrBucketNameRequired = bolt.ErrBucketNameRequired

	// ErrKeyRequired is returned when inserting a zero-length key.
	ErrKeyRequired = bolt.ErrKeyRequired

	// ErrKeyTooLarge is returned when inserting a key that is larger than MaxKeySize.
	ErrKeyTooLarge = bolt.ErrKeyTooLarge

	// ErrValueTooLarge is returned when inserting a value that is larger than MaxValueSize.
	ErrValueTooLarge = bolt.ErrValueTooLarge

	// ErrIncompatibleValue is returned when trying create or delete a bucket
	// on an existing non-bucket key or when trying to create or delete a
	// non-bucket key on an existing bucket key.
	ErrIncompatibleValue = bolt.ErrIncompatibleValue
)

These errors can occur when putting or deleting a value or a bucket.

Functions

func ToSlice

func ToSlice(v interface{}) ([]byte, error)

ToSlice v must be []byte, string, Name, Byter or a fixed width struct.

Types

type Bucket

type Bucket struct {
	OrigBucket
	// contains filtered or unexported fields
}

Bucket .

func (*Bucket) Bucket

func (bk *Bucket) Bucket(name []byte) *Bucket

Bucket .

func (*Bucket) CreateBucket

func (bk *Bucket) CreateBucket(key []byte) (*Bucket, error)

CreateBucket .

func (*Bucket) CreateBucketIfNotExists

func (bk *Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error)

CreateBucketIfNotExists .

func (*Bucket) Cursor

func (bk *Bucket) Cursor() *Cursor

Cursor .

func (*Bucket) Delete

func (bk *Bucket) Delete(key []byte) error

Delete .

Example
// Open the database.
db, err := bolt.Open(tempfile(), 0666, nil)
if err != nil {
	log.Fatal(err)
}
defer os.Remove(db.Path())

// Start a write transaction.
if err := db.Update(func(tx *bolt.Tx) error {
	// Create a bucket.
	b, err := tx.CreateBucket([]byte("widgets"))
	if err != nil {
		return err
	}

	// Set the value "bar" for the key "foo".
	if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
		return err
	}

	// Retrieve the key back from the database and verify it.
	value := b.Get([]byte("foo"))
	fmt.Printf("The value of 'foo' was: %s\n", value)

	return nil
}); err != nil {
	log.Fatal(err)
}

// Delete the key in a different write transaction.
if err := db.Update(func(tx *bolt.Tx) error {
	return tx.Bucket([]byte("widgets")).Delete([]byte("foo"))
}); err != nil {
	log.Fatal(err)
}

// Retrieve the key again.
if err := db.View(func(tx *bolt.Tx) error {
	value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
	if value == nil {
		fmt.Printf("The value of 'foo' is now: nil\n")
	}
	return nil
}); err != nil {
	log.Fatal(err)
}

// Close database to release file lock.
if err := db.Close(); err != nil {
	log.Fatal(err)
}
Output:

The value of 'foo' was: bar
The value of 'foo' is now: nil

func (*Bucket) DeleteBucket

func (bk *Bucket) DeleteBucket(key []byte) error

DeleteBucket .

func (*Bucket) Put

func (bk *Bucket) Put(key []byte, value []byte) error

Put .

Example
// Open the database.
db, err := bolt.Open(tempfile(), 0666, nil)
if err != nil {
	log.Fatal(err)
}
defer os.Remove(db.Path())

// Start a write transaction.
if err := db.Update(func(tx *bolt.Tx) error {
	// Create a bucket.
	b, err := tx.CreateBucket([]byte("widgets"))
	if err != nil {
		return err
	}

	// Set the value "bar" for the key "foo".
	if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
		return err
	}
	return nil
}); err != nil {
	log.Fatal(err)
}

// Read value back in a different read-only transaction.
if err := db.View(func(tx *bolt.Tx) error {
	value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
	fmt.Printf("The value of 'foo' is: %s\n", value)
	return nil
}); err != nil {
	log.Fatal(err)
}

// Close database to release file lock.
if err := db.Close(); err != nil {
	log.Fatal(err)
}
Output:

The value of 'foo' is: bar

func (*Bucket) Tx

func (bk *Bucket) Tx() *Tx

Tx .

type Byter

type Byter interface {
	Bytes() []byte
}

Byter .

type Cursor

type Cursor struct {
	OrigCursor
	// contains filtered or unexported fields
}

Cursor .

Example
// Open the database.
db, err := bolt.Open(tempfile(), 0666, nil)
if err != nil {
	log.Fatal(err)
}
defer os.Remove(db.Path())

// Start a read-write transaction.
if err := db.Update(func(tx *bolt.Tx) error {
	// Create a new bucket.
	b, err := tx.CreateBucket([]byte("animals"))
	if err != nil {
		return err
	}

	// Insert data into a bucket.
	if err := b.Put([]byte("dog"), []byte("fun")); err != nil {
		log.Fatal(err)
	}
	if err := b.Put([]byte("cat"), []byte("lame")); err != nil {
		log.Fatal(err)
	}
	if err := b.Put([]byte("liger"), []byte("awesome")); err != nil {
		log.Fatal(err)
	}

	// Create a cursor for iteration.
	c := b.Cursor()

	// Iterate over items in sorted key order. This starts from the
	// first key/value pair and updates the k/v variables to the
	// next key/value on each iteration.
	//
	// The loop finishes at the end of the cursor when a nil key is returned.
	for k, v := c.First(); k != nil; k, v = c.Next() {
		fmt.Printf("A %s is %s.\n", k, v)
	}

	return nil
}); err != nil {
	log.Fatal(err)
}

if err := db.Close(); err != nil {
	log.Fatal(err)
}
Output:

A cat is lame.
A dog is fun.
A liger is awesome.
Example (Reverse)
// Open the database.
db, err := bolt.Open(tempfile(), 0666, nil)
if err != nil {
	log.Fatal(err)
}
defer os.Remove(db.Path())

// Start a read-write transaction.
if err := db.Update(func(tx *bolt.Tx) error {
	// Create a new bucket.
	b, err := tx.CreateBucket([]byte("animals"))
	if err != nil {
		return err
	}

	// Insert data into a bucket.
	if err := b.Put([]byte("dog"), []byte("fun")); err != nil {
		log.Fatal(err)
	}
	if err := b.Put([]byte("cat"), []byte("lame")); err != nil {
		log.Fatal(err)
	}
	if err := b.Put([]byte("liger"), []byte("awesome")); err != nil {
		log.Fatal(err)
	}

	// Create a cursor for iteration.
	c := b.Cursor()

	// Iterate over items in reverse sorted key order. This starts
	// from the last key/value pair and updates the k/v variables to
	// the previous key/value on each iteration.
	//
	// The loop finishes at the beginning of the cursor when a nil key
	// is returned.
	for k, v := c.Last(); k != nil; k, v = c.Prev() {
		fmt.Printf("A %s is %s.\n", k, v)
	}

	return nil
}); err != nil {
	log.Fatal(err)
}

// Close the database to release the file lock.
if err := db.Close(); err != nil {
	log.Fatal(err)
}
Output:

A liger is awesome.
A dog is fun.
A cat is lame.

func (*Cursor) Bucket

func (c *Cursor) Bucket() *Bucket

Bucket .

type DB

type DB struct {
	*bolt.DB
	// contains filtered or unexported fields
}

DB .

Example (BoltronIndexTimeJSON)
// Open the database.
db, err := bolt.Open(tempfile(), 0666, nil)
if err != nil {
	log.Fatal(err)
}
defer os.Remove(db.Path())
defer db.Close()

// sample struct that holds the data
type data struct {
	ID    string    `json:"id,omitempty"`
	Name  string    `json:"name,omitempty"`
	Score float64   `json:"score,omitempty"`
	At    time.Time `json:"at,omitempty"`
}

err = db.AddIndex(bolt.NewIndex("times", func(k, v []byte) [][]byte {
	// this is a sample index that indexes the data if
	// the key starts with "data:" and
	// the value is a valid json and
	// it has a field named "at" containing a valid time.Time representation.
	// otherwise it will get ignored and not be indexed.

	// ignoring any key that has not a 'data:' prefix
	// by returning nil. this way, other kinds of data
	// would not be indexed by this index.
	if !bytes.HasPrefix(k, []byte("data:")) {
		return nil
	}

	// check if v is valid json
	js := string(v)
	if !gjson.Valid(js) {
		return nil
	}

	// getting the event time
	res := gjson.Get(string(v), "at")
	at := res.Time()
	if at.IsZero() {
		return nil
	}

	return [][]byte{[]byte(at.Format("20060102150405"))}
}))
if err != nil {
	log.Fatal(err)
}

err = db.Update(func(tx *bolt.Tx) error {
	// inserting a sample record
	d := data{
		ID:    "data:000010",
		Name:  "Kaveh",
		Score: 71,
		At:    time.Now(),
	}
	js, err := json.Marshal(&d)
	if err != nil {
		return err
	}
	bk, err := tx.CreateBucketIfNotExists([]byte("data"))
	if err != nil {
		return err
	}
	return bk.Put([]byte(d.ID), js)
})
if err != nil {
	log.Fatal(err)
}

var found *data
err = db.View(func(tx *bolt.Tx) error {
	// searching for first records between yesterday (in current hour) and tomorrow
	indexBucket := tx.Bucket([]byte("times"))
	now := time.Now()
	yesterday := []byte(now.Add(time.Hour * 24 * -1).Format("20060102150405"))
	tomorrow := []byte(now.Add(time.Hour * 24).Format("20060102150405"))
	c := indexBucket.Cursor()
	prefix := yesterday
	var id []byte
	for k, v := c.Seek(prefix); k != nil && bytes.Compare(k, tomorrow) <= 0; k, v = c.Next() {
		id = v
		break
	}
	dataBucket := tx.Bucket([]byte("data"))
	v := dataBucket.Get(id)
	if len(v) == 0 {
		log.Fatal("not found")
	}
	found = new(data)
	return json.Unmarshal(v, found)
})
if err != nil {
	log.Fatal(err)
}

fmt.Println(found.ID)
Output:

data:000010

func Open

func Open(path string, mode os.FileMode, options *Options) (*DB, error)

Open creates and opens a database at the given path. If the file does not exist then it will be created automatically. Passing in nil options will cause Bolt to open the database with the default options.

func (*DB) AddIndex

func (db *DB) AddIndex(indexes ...*Index) error

AddIndex adds the indexs

func (*DB) Batch

func (db *DB) Batch(fn func(*Tx) error) error

Batch .

func (*DB) Begin

func (db *DB) Begin(writable bool) (*Tx, error)

Begin .

Example (ReadOnly)
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	bolt "github.com/dc0d/boltron"
)

func main() {
	// Open the database.
	db, err := bolt.Open(tempfile(), 0666, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer os.Remove(db.Path())

	// Create a bucket using a read-write transaction.
	if err = db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucket([]byte("widgets"))
		return err
	}); err != nil {
		log.Fatal(err)
	}

	// Create several keys in a transaction.
	tx, err := db.Begin(true)
	if err != nil {
		log.Fatal(err)
	}
	b := tx.Bucket([]byte("widgets"))
	if err = b.Put([]byte("john"), []byte("blue")); err != nil {
		log.Fatal(err)
	}
	if err = b.Put([]byte("abby"), []byte("red")); err != nil {
		log.Fatal(err)
	}
	if err = b.Put([]byte("zephyr"), []byte("purple")); err != nil {
		log.Fatal(err)
	}
	if err = tx.Commit(); err != nil {
		log.Fatal(err)
	}

	// Iterate over the values in sorted key order.
	tx, err = db.Begin(false)
	if err != nil {
		log.Fatal(err)
	}
	c := tx.Bucket([]byte("widgets")).Cursor()
	for k, v := c.First(); k != nil; k, v = c.Next() {
		fmt.Printf("%s likes %s\n", k, v)
	}

	if err = tx.Rollback(); err != nil {
		log.Fatal(err)
	}

	if err = db.Close(); err != nil {
		log.Fatal(err)
	}

}

// tempfile returns a temporary file path.
func tempfile() string {
	f, err := ioutil.TempFile("", "bolt-")
	if err != nil {
		panic(err)
	}
	if err := f.Close(); err != nil {
		panic(err)
	}
	if err := os.Remove(f.Name()); err != nil {
		panic(err)
	}
	return f.Name()
}
Output:

abby likes red
john likes blue
zephyr likes purple

func (*DB) DropIndex

func (db *DB) DropIndex(indexes ...string) (_err error)

DropIndex .

func (*DB) RebuildIndex

func (db *DB) RebuildIndex(name string) error

RebuildIndex .

func (*DB) Update

func (db *DB) Update(fn func(*Tx) error) error

Update .

Example
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	bolt "github.com/dc0d/boltron"
)

func main() {
	// Open the database.
	db, err := bolt.Open(tempfile(), 0666, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer os.Remove(db.Path())

	// Execute several commands within a read-write transaction.
	if err := db.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucket([]byte("widgets"))
		if err != nil {
			return err
		}
		if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
			return err
		}
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	// Read the value back from a separate read-only transaction.
	if err := db.View(func(tx *bolt.Tx) error {
		value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
		fmt.Printf("The value of 'foo' is: %s\n", value)
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	// Close database to release the file lock.
	if err := db.Close(); err != nil {
		log.Fatal(err)
	}

}

// tempfile returns a temporary file path.
func tempfile() string {
	f, err := ioutil.TempFile("", "bolt-")
	if err != nil {
		panic(err)
	}
	if err := f.Close(); err != nil {
		panic(err)
	}
	if err := os.Remove(f.Name()); err != nil {
		panic(err)
	}
	return f.Name()
}
Output:

The value of 'foo' is: bar

func (*DB) View

func (db *DB) View(fn func(*Tx) error) error

View .

Example
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	bolt "github.com/dc0d/boltron"
)

func main() {
	// Open the database.
	db, err := bolt.Open(tempfile(), 0666, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer os.Remove(db.Path())

	// Insert data into a bucket.
	if err := db.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucket([]byte("people"))
		if err != nil {
			return err
		}
		if err := b.Put([]byte("john"), []byte("doe")); err != nil {
			return err
		}
		if err := b.Put([]byte("susy"), []byte("que")); err != nil {
			return err
		}
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	// Access data from within a read-only transactional block.
	if err := db.View(func(tx *bolt.Tx) error {
		v := tx.Bucket([]byte("people")).Get([]byte("john"))
		fmt.Printf("John's last name is %s.\n", v)
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	// Close database to release the file lock.
	if err := db.Close(); err != nil {
		log.Fatal(err)
	}

}

// tempfile returns a temporary file path.
func tempfile() string {
	f, err := ioutil.TempFile("", "bolt-")
	if err != nil {
		panic(err)
	}
	if err := f.Close(); err != nil {
		panic(err)
	}
	if err := os.Remove(f.Name()); err != nil {
		panic(err)
	}
	return f.Name()
}
Output:

John's last name is doe.

type Index

type Index struct {
	// contains filtered or unexported fields
}

Index provide either gjson pattern(s) or selector (not both)

func NewIndex

func NewIndex(name string, selector func(k, v []byte) [][]byte) *Index

NewIndex .

type KeyMaker

type KeyMaker struct {
	// contains filtered or unexported fields
}

KeyMaker .

func (KeyMaker) Bytes

func (k KeyMaker) Bytes(sep []byte) []byte

Bytes .

func (*KeyMaker) Write

func (k *KeyMaker) Write(v interface{}) error

type Name

type Name string

Name uses an string and provides []byte when needed. Storing []byte in a variable is problematic.

func (Name) Bytes

func (n Name) Bytes() []byte

Bytes .

type Options

type Options = bolt.Options

Options represents the options that can be set when opening a database.

type OrigBucket

type OrigBucket = *bolt.Bucket

OrigBucket .

type OrigCursor

type OrigCursor = *bolt.Cursor

OrigCursor .

type Stats

type Stats = bolt.Stats

Stats represents statistics about the database.

type Tx

type Tx struct {
	*bolt.Tx
	// contains filtered or unexported fields
}

Tx .

func (*Tx) Bucket

func (tx *Tx) Bucket(name []byte) *Bucket

Bucket .

func (*Tx) CreateBucket

func (tx *Tx) CreateBucket(name []byte) (*Bucket, error)

CreateBucket .

func (*Tx) CreateBucketIfNotExists

func (tx *Tx) CreateBucketIfNotExists(name []byte) (*Bucket, error)

CreateBucketIfNotExists .

func (*Tx) DB

func (tx *Tx) DB() *DB

DB .

func (*Tx) DeleteBucket

func (tx *Tx) DeleteBucket(name []byte) error

DeleteBucket .

func (*Tx) ForEach

func (tx *Tx) ForEach(fn func(name []byte, b *Bucket) error) error

ForEach .

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL