memdb

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Feb 5, 2023 License: MIT Imports: 5 Imported by: 0

README

memdb

memdb is an easy-to-use in-memory database for Go programming language. It provides a simple and efficient way to store and retrieve data in memory, with features such as type safety, transactions, rich indexing, and sorting.

Features

  • Type Safety - memdb uses generics to provide strong type safety, reducing issues that can be found at compile time. This ensures that the data you are working with is of the correct type, and minimizes the chance of runtime errors.
  • Transactions - Transactions in memdb can span multiple tables, and are applied atomically. This means that all changes made within a transaction are either applied together or not at all, ensuring data consistency and integrity.
  • Rich Indexing - memdb supports single and compound indexes, allowing you to filter and sort data based on multiple properties. This makes it easy to retrieve specific data from the database quickly and efficiently.
  • Sorting - memdb allows you to sort query results by different entry properties, making it easy to organize and present data in a specific order.

Project status

Currently, this library is not stable and should not be used for production. It is still under development and may contain bugs or unfinished features. However, it is open for contributions, bugs reporting and feature requests.

Installation

To install memdb, you can use the go get command:

go get github.com/knobz-io/memdb

Then, you can import the package in your Go code:

import "github.com/knobz-io/memdb"

Usage

To start using memdb, you'll need to first create a table schema for your data models using the memdb.Table[V] struct. This schema will define the structure of your data, and specify any indexes that you want to create on it. Once the table schema is created, you can use it to initialize a new *memdb.DB instance.

Creating table schema

In order to use memdb, you'll need to define your data models in Go. These will be the types that you'll store in the database. For example, you could have a User struct like this:

type Status int

const (
    Active Status = iota
    Suspended
    Banned
)

type User struct {
    ID       int
    Status   Status
    Email    string
    FullName string
}

Once you have your data models defined, you can create a table schema for them using the memdb.Table[V] struct. The schema will specify how the data is stored in the database, and will define any indexes that you want to create on it. For example, you could create a UserTable schema like this::


type UserTable struct {
	memdb.Table[*User]
	// indexes
	status   *memdb.IntIndex[*User]
	email    *memdb.StringIndex[*User]
	fullName *memdb.StringIndex[*User]
}

// ID is a helper function for preparing
// user primary key from int
func (UserTable) ID(id int) memdb.Key {
	return memdb.IntKey(id)
}

func makeUserTable() UserTable {
	table := memdb.NewTable(func(usr *User) memdb.Key {
		return memdb.IntKey(usr.ID)
	})
	// preparing indexes
	table, status := table.IndexInt(func(usr *User) int {
		return int(usr.Status)
	})
	table, email := table.IndexString(func(usr *User) string {
		return usr.Email
	})
	table, fullName := table.IndexString(func(usr *User) string {
		return usr.FullName
	})
	return UserTable{
		Table:    table,
		status:   status,
		email:    email,
		fullName: fullName,
	}
}
Initializing database

Once you have created a table schema, you can use it to initialize a new *memdb.DB instance. The Init function takes a variable number of table schemas as arguments, allowing you to create multiple tables in a single database.

users := makeUserTable()
db, err := memdb.Init(users)
Inserting data

To insert data into the database, you'll need to start a write transaction using the db.WriteTx() method. Once you have a transaction, you can use the Set or SetMulti method on the table schema to insert one or multiple entries into the table.

// start a write transaction
tx := db.WriteTx()

// insert one entry
users.Set(tx, &User{
    ID:       1,
    Status:   Active,
    Email:    "john.doe@example.com",
    FullName: "John Doe",
})

// or insert multiple entires
users.SetMulti(tx, []*User{
    {
        ID:       2,
        Status:   Active,
        Email:    "david.rich@example.com",
        FullName: "David Rich",
    },
    {
        ID:       3,
        Status:   Suspended,
        Email:    "matt.smith@example.com",
        FullName: "Matt Smith",
    },
    {
        ID:       4,
        Status:   Active,
        Email:    "jack.thompson@example.com",
        FullName: "Jack Thompson",
    },
})

// commit changes
tx.Commit()
Deleting data

To delete data from the database, you'll need to start a write transaction using the db.WriteTx() method. Once you have a transaction, you can use the Del or DelMulti method on the table schema to delete one or multiple entries from the table.

// start a write transaction
tx := db.WriteTx()

// delete single entry where ID=1
err := users.Del(tx, users.ID(1))

// or delete multiple entries where ID=1, ID=2
err := users.DelMulti(tx, []memdb.Key{
    users.ID(1),
    users.ID(2),
})

// commit changes
tx.Commit()
Retrieving single entry

To retrieve a specific entry from the table using its primary key, you'll need to start a read-only transaction using the db.ReadTx() method. Once you have a transaction, you can use the Get method on the table schema to retrieve the entry.

// start read-only transaction
tx := db.ReadTx()
// retrieving entry with ID=3
usr, err := users.Get(tx, users.ID(3))
if err == memdb.ErrNotFound {
    // handle not found
} else if err != nil {
    panic(err)
}
Retrieving multiple entries

The Select method on the table schema can be used to retrieve multiple entries from the table. The returned value is a query object that can be used to filter and sort the results.

// start read-only transaction
tx := db.ReadTx()

// retrieve all entries
list, err := users.Select(tx).All()

This will retrieve all entries from the table, without any filtering or sorting.

You can also use the Page method to retrieve a specific page of entries from the table.

// return limited number of entries
// from the speciffic point
limit := 10
offset := 5
list, err := users.Select(tx).Page(limit, offset)

This will retrieve a specific page of the entries with limit number of entries starting from offset.

For iterating over the table entries one by one, you can create a cursor and iterate over it.

// create new cursor
c, err := users.Select(tx).Cursor()
if err != nil {
    panic(err)
}
// iterate over table entries
for usr, ok := c.First(); ok; usr, ok = c.Next() {
    fmt.Println("id:", usr.ID, "email:", usr.Email)
}

This will allow you to retrieve entries one by one and do something with them. This can be useful for large tables that you don't want to load into memory all at once.

Please note that, these examples are for retriving all the entries, if you want to filter the entries based on certain condition, you can use the Where method on the query object.

Filtering

To retrieve a set of entries from the table based on certain conditions, you can use the Select method on the table schema to create a query, and then use various filter methods to specify the conditions.

// start read-only transaction
tx := db.ReadTx()
// filter all entries where Status=Active
list, err := users.Select(tx).
    Where(
        users.status.Is(int(Active)),
    ).
    All()
Sorting

To retrieve a set of entries from the table and sort them based on certain properties, you can use the Select method on the table schema to create a query, and then use various sort methods to specify the sorting order.

// start read-only transaction
tx := db.ReadTx()
// return a list of all users sorted all users
// sorted by FullName property in ascending order
list, err := users.Select(tx).
    OrderBy(users.fullName.Asc()).
    All()

In this example, the OrderBy method is used to sort the query results by the FullName property in ascending order. The Asc() method is used to specify the sorting order.

You can chain multiple sorting conditions by adding them to OrderBy method.

list, err := users.Select(tx).
    OrderBy(
        users.fullName.Asc(),
        users.email.Desc(),
    ).
    All()

This will sort the query results by the FullName property in ascending order, then if two or more entries have the same FullName it will sort them by the Email property in descending order.

For more information on how to use memdb, please refer to the Godoc.

Contributing

If you're interested in contributing to memdb, please read the contributing guidelines before submitting a pull request.

License

memdb is licensed under the MIT License.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotFound = errors.New("memdb: not found")
)

Functions

This section is empty.

Types

type BinaryIndex added in v0.1.2

type BinaryIndex[V any] struct {
	// contains filtered or unexported fields
}

func (*BinaryIndex[V]) Asc added in v0.1.3

func (f *BinaryIndex[V]) Asc() *OrderRule[V]

func (*BinaryIndex[V]) Desc added in v0.1.3

func (f *BinaryIndex[V]) Desc() *OrderRule[V]

func (*BinaryIndex[V]) GreaterThan added in v0.1.2

func (f *BinaryIndex[V]) GreaterThan(v []byte) *GreaterThanCond[V]

func (*BinaryIndex[V]) GreaterThanOrEqual added in v0.1.2

func (f *BinaryIndex[V]) GreaterThanOrEqual(v []byte) *GreaterThanOrEqualCond[V]

func (*BinaryIndex[V]) Is added in v0.1.2

func (f *BinaryIndex[V]) Is(v []byte) *EqualCond[V]

func (*BinaryIndex[V]) KeyOf added in v0.1.2

func (f *BinaryIndex[V]) KeyOf(v V) Key

func (*BinaryIndex[V]) LessThan added in v0.1.2

func (f *BinaryIndex[V]) LessThan(v []byte) *LessThanCond[V]

func (*BinaryIndex[V]) LessThanOrEqual added in v0.1.2

func (f *BinaryIndex[V]) LessThanOrEqual(v []byte) *LessThanOrEqualCond[V]

type BinaryKey

type BinaryKey []byte

func (BinaryKey) Bytes

func (b BinaryKey) Bytes() []byte

type BoolIndex added in v0.1.2

type BoolIndex[V any] struct {
	// contains filtered or unexported fields
}

func (*BoolIndex[V]) IsFalse added in v0.1.2

func (f *BoolIndex[V]) IsFalse() *EqualCond[V]

func (*BoolIndex[V]) IsTrue added in v0.1.2

func (f *BoolIndex[V]) IsTrue() *EqualCond[V]

func (*BoolIndex[V]) KeyOf added in v0.1.2

func (f *BoolIndex[V]) KeyOf(v V) Key

type BoolKey

type BoolKey bool

func (BoolKey) Bytes

func (b BoolKey) Bytes() []byte

type CombinedIndex added in v0.1.2

type CombinedIndex[V any] struct {
	// contains filtered or unexported fields
}

func (*CombinedIndex[V]) Is added in v0.1.2

func (f *CombinedIndex[V]) Is(k CombinedKey) *EqualCond[V]

func (*CombinedIndex[V]) KeyOf added in v0.1.2

func (f *CombinedIndex[V]) KeyOf(v V) Key

type CombinedKey added in v0.1.2

type CombinedKey []Key

func (CombinedKey) Bytes added in v0.1.2

func (mk CombinedKey) Bytes() []byte

type Cond

type Cond[V any] interface {
	Matches(v V) bool
}

type CondFunc

type CondFunc[V any] func(V) bool

func (CondFunc[V]) Matches

func (fn CondFunc[V]) Matches(v V) bool

type DB

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

func Init

func Init(tables ...TableType) (*DB, error)

func (*DB) ReadTx

func (db *DB) ReadTx() *Txn

func (*DB) Tx

func (db *DB) Tx(write bool) *Txn

func (*DB) WriteTx

func (db *DB) WriteTx() *Txn

type EqualCond

type EqualCond[V any] struct {
	// contains filtered or unexported fields
}

func (*EqualCond[V]) Matches

func (c *EqualCond[V]) Matches(v V) bool

type FloatIndex added in v0.1.2

type FloatIndex[V any] struct {
	// contains filtered or unexported fields
}

func (*FloatIndex[V]) Asc added in v0.1.3

func (f *FloatIndex[V]) Asc() *OrderRule[V]

func (*FloatIndex[V]) Desc added in v0.1.3

func (f *FloatIndex[V]) Desc() *OrderRule[V]

func (*FloatIndex[V]) GreaterThan added in v0.1.2

func (f *FloatIndex[V]) GreaterThan(v float64) *GreaterThanCond[V]

func (*FloatIndex[V]) GreaterThanOrEqual added in v0.1.2

func (f *FloatIndex[V]) GreaterThanOrEqual(v float64) *GreaterThanOrEqualCond[V]

func (*FloatIndex[V]) Is added in v0.1.2

func (f *FloatIndex[V]) Is(v float64) *EqualCond[V]

func (*FloatIndex[V]) KeyOf added in v0.1.2

func (f *FloatIndex[V]) KeyOf(v V) Key

func (*FloatIndex[V]) LessThan added in v0.1.2

func (f *FloatIndex[V]) LessThan(v float64) *LessThanCond[V]

func (*FloatIndex[V]) LessThanOrEqual added in v0.1.2

func (f *FloatIndex[V]) LessThanOrEqual(v float64) *LessThanOrEqualCond[V]

type FloatKey

type FloatKey float64

func (FloatKey) Bytes

func (f FloatKey) Bytes() []byte

type GreaterThanCond

type GreaterThanCond[V any] struct {
	// contains filtered or unexported fields
}

func (*GreaterThanCond[V]) Matches

func (c *GreaterThanCond[V]) Matches(v V) bool

type GreaterThanOrEqualCond

type GreaterThanOrEqualCond[V any] struct {
	// contains filtered or unexported fields
}

func (*GreaterThanOrEqualCond[V]) Matches

func (c *GreaterThanOrEqualCond[V]) Matches(v V) bool

type Index added in v0.1.3

type Index[V any] interface {
	KeyOf(v V) Key
	// contains filtered or unexported methods
}

type IndexMap

type IndexMap[V any] struct {
	// contains filtered or unexported fields
}

type IntIndex added in v0.1.2

type IntIndex[V any] struct {
	// contains filtered or unexported fields
}

func (*IntIndex[V]) Asc added in v0.1.3

func (f *IntIndex[V]) Asc() *OrderRule[V]

func (*IntIndex[V]) Desc added in v0.1.3

func (f *IntIndex[V]) Desc() *OrderRule[V]

func (*IntIndex[V]) Is added in v0.1.2

func (f *IntIndex[V]) Is(v int) *EqualCond[V]

func (*IntIndex[V]) IsGreaterThan added in v0.1.2

func (f *IntIndex[V]) IsGreaterThan(v int) *GreaterThanCond[V]

func (*IntIndex[V]) IsGreaterThanOrEqual added in v0.1.2

func (f *IntIndex[V]) IsGreaterThanOrEqual(v int) *GreaterThanOrEqualCond[V]

func (*IntIndex[V]) IsLessThan added in v0.1.2

func (f *IntIndex[V]) IsLessThan(v int) *LessThanCond[V]

func (*IntIndex[V]) IsLessThanOrEqual added in v0.1.2

func (f *IntIndex[V]) IsLessThanOrEqual(v int) *LessThanOrEqualCond[V]

func (*IntIndex[V]) KeyOf added in v0.1.2

func (f *IntIndex[V]) KeyOf(v V) Key

type IntKey

type IntKey int

func (IntKey) Bytes

func (i IntKey) Bytes() []byte

type Key

type Key interface {
	Bytes() []byte
}

type KeyFunc

type KeyFunc[V any] func(V) Key

type LessThanCond

type LessThanCond[V any] struct {
	// contains filtered or unexported fields
}

func (*LessThanCond[V]) Matches

func (c *LessThanCond[V]) Matches(v V) bool

type LessThanOrEqualCond

type LessThanOrEqualCond[V any] struct {
	// contains filtered or unexported fields
}

func (*LessThanOrEqualCond[V]) Matches

func (c *LessThanOrEqualCond[V]) Matches(v V) bool

type OrderDirection

type OrderDirection int
const (
	Asc OrderDirection = iota
	Desc
)

type OrderRule added in v0.1.3

type OrderRule[V any] struct {
	// contains filtered or unexported fields
}

type StringIndex added in v0.1.2

type StringIndex[V any] struct {
	// contains filtered or unexported fields
}

func (*StringIndex[V]) Asc added in v0.1.3

func (f *StringIndex[V]) Asc() *OrderRule[V]

func (*StringIndex[V]) Desc added in v0.1.3

func (f *StringIndex[V]) Desc() *OrderRule[V]

func (*StringIndex[V]) GreaterThan added in v0.1.2

func (f *StringIndex[V]) GreaterThan(v string) *GreaterThanCond[V]

func (*StringIndex[V]) GreaterThanOrEqual added in v0.1.2

func (f *StringIndex[V]) GreaterThanOrEqual(v string) *GreaterThanOrEqualCond[V]

func (*StringIndex[V]) Is added in v0.1.2

func (f *StringIndex[V]) Is(v string) *EqualCond[V]

func (*StringIndex[V]) KeyOf added in v0.1.2

func (f *StringIndex[V]) KeyOf(v V) Key

func (*StringIndex[V]) LessThan added in v0.1.2

func (f *StringIndex[V]) LessThan(v string) *LessThanCond[V]

func (*StringIndex[V]) LessThanOrEqual added in v0.1.2

func (f *StringIndex[V]) LessThanOrEqual(v string) *LessThanOrEqualCond[V]

type StringKey

type StringKey string

func (StringKey) Bytes

func (s StringKey) Bytes() []byte

type Table

type Table[V any] struct {
	// contains filtered or unexported fields
}

func NewTable

func NewTable[V any](fn KeyFunc[V]) Table[V]

func (Table[V]) Del

func (t Table[V]) Del(tx *Txn, pk Key) error

func (Table[V]) DelMulti

func (t Table[V]) DelMulti(tx *Txn, pks []Key) error

func (Table[V]) Get

func (t Table[V]) Get(tx *Txn, id Key) (V, error)

func (Table[V]) IndexBinary

func (t Table[V]) IndexBinary(fn func(V) []byte) (Table[V], *BinaryIndex[V])

func (Table[V]) IndexBool

func (t Table[V]) IndexBool(fn func(V) bool) (Table[V], *BoolIndex[V])

func (Table[V]) IndexFloat

func (t Table[V]) IndexFloat(fn func(V) float64) (Table[V], *FloatIndex[V])

func (Table[V]) IndexInt

func (t Table[V]) IndexInt(fn func(V) int) (Table[V], *IntIndex[V])

func (Table[V]) IndexMultiple

func (t Table[V]) IndexMultiple(fn func(V) CombinedKey) (Table[V], *CombinedIndex[V])

func (Table[V]) IndexString

func (t Table[V]) IndexString(fn func(V) string) (Table[V], *StringIndex[V])

func (Table[V]) Select added in v0.1.1

func (t Table[V]) Select(tx *Txn) *TableLister[V]

func (Table[V]) Set

func (t Table[V]) Set(tx *Txn, v V) error

func (Table[V]) SetMulti

func (t Table[V]) SetMulti(tx *Txn, vs []V) error

type TableCursor

type TableCursor[V any] struct {
	// contains filtered or unexported fields
}

func (*TableCursor[V]) First

func (t *TableCursor[V]) First() (V, bool)

func (*TableCursor[V]) Last

func (t *TableCursor[V]) Last() (V, bool)

func (*TableCursor[V]) Next

func (t *TableCursor[V]) Next() (V, bool)

func (*TableCursor[V]) Prev

func (t *TableCursor[V]) Prev() (V, bool)

func (*TableCursor[V]) Seek

func (t *TableCursor[V]) Seek(id Key) (V, bool)

type TableLister

type TableLister[V any] struct {
	// contains filtered or unexported fields
}

func (*TableLister[V]) All added in v0.1.3

func (t *TableLister[V]) All() ([]V, error)

func (*TableLister[V]) Asc

func (t *TableLister[V]) Asc() *TableLister[V]

func (*TableLister[V]) Count

func (t *TableLister[V]) Count() (int, error)

func (*TableLister[V]) Cursor

func (t *TableLister[V]) Cursor() (*TableCursor[V], error)

func (*TableLister[V]) Desc

func (t *TableLister[V]) Desc() *TableLister[V]

func (*TableLister[V]) One

func (t *TableLister[V]) One() (V, error)

func (*TableLister[V]) OrderBy

func (t *TableLister[V]) OrderBy(order Index[V]) *TableLister[V]

func (*TableLister[V]) Page

func (t *TableLister[V]) Page(limit, offset int) ([]V, error)

func (*TableLister[V]) Where

func (t *TableLister[V]) Where(conds ...Cond[V]) *TableLister[V]

type TableSelection

type TableSelection[V any] struct {
	// contains filtered or unexported fields
}

type TableType

type TableType interface {
	// contains filtered or unexported methods
}

type Txn

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

func (*Txn) Abort

func (tx *Txn) Abort()

func (*Txn) Commit

func (tx *Txn) Commit()

Jump to

Keyboard shortcuts

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