ent

package module
v0.2.11 Latest Latest
Warning

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

Go to latest
Published: Nov 21, 2020 License: ISC Imports: 11 Imported by: 0

README

Simple data entities for Go

GitHub tag (latest SemVer) PkgGoDev Go Report Card

Ent, short for "entities", is data persistence for Go, using plain Go structs.

Features:

  • No extra "description" files needed: just add tags to your Go structs.

  • Automatically versioned ents.

  • Automatic unique and non-unique secondary indexes (e.g. look up an account by email) including compound indexes.

  • Transactional edits — a change either fully succeeds or not at all.

  • Multiple storage backends with a small API for using custom storage.

  • CouchDB-like get-modify-put — when "putting" it back, if the ent has changed by someone else since you loaded it, there will be a version conflict error and no changes will be made.

  • Uses code generation instead of reflection to minimize magic — read the go fmt-formatted generated code to understand exactly what is happening.

  • Generated code is included in your documentation (e.g. via go doc)

Tutorial

Ent uses Go structs. By adding ent.EntBase as the first embedded field in a struct, you have made it into an ent! You can find a complete example of this code in examples/tutorial.

Let's start by defining an "Account" struct type:

//go:generate entgen
package main

import "github.com/rsms/ent"

type AccountKind int32

const (
  AccountRestricted = AccountKind(iota)
  AccountMember
  AccountAdmin
)

type Account struct {
  ent.EntBase `account` // type name, used to store these kinds of ents
  name        string
  displayName string      `ent:"alias"`   // use a different field name for storage
  email       string      `ent:",unique"` // maintain a unique index for this field
  kind        AccountKind `ent:",index"`  // maintain a (non-unique) index for this field
}

When this file changes we should run entgen (or go generate if we have a //go:generate comment in the file.) Doing so causes a few methods, functions and data to be generated for the Account type, which makes it a fully-functional data entity.

You can either run entgen from source or go modules, or install it with go get github.com/rsms/ent/entgen. See #entgen for more details.

We can now create, load, query, update and delete accounts. But let's start by making one and just printing it:

func main() {
  a := &Account{
    name:  "Jane",
    alias: "j-town",
    email: "jane@example.com",
    kind:  AccountMember,
  }
  println(a.String())
}

If we build & run this we should see the following output:

{
  _ver:  "0",
  _id:   "0",
  name:  "Jane",
  alias: "j-town",
  email: "jane@example.com",
  kind:  1
}

Notice a couple of things:

  • The String method returns a JSON-like representation

  • There are two implicit fields: _ver and _id. These represent the current version and id of an ent, respectively. These values can be accessed via the Version() and Id() methods. A value of 0 (zero) means "not yet assigned".

  • The displayName field is called alias; renamed by the ent field tag.

  • Field order matches our struct definition.

Now let's store this account in a database. This is really what ent is about — data persistence. We start this example by creating a place to store ents, a storage. Here we use an in-memory storage implementation mem.EntStorage but there are other kinds, like Redis.

import "github.com/rsms/ent/mem"

  // add to our main function:
  estore := mem.NewEntStorage()
  if err := a.Create(estore); err != nil {
    panic(err)
  }
  println(a.String())

Our account is now saved. The output from the last print statement now contains non-zero version & id:

{
  _ver:  "1",
  _id:   "1",
  name:  "Jane",
  ...

Another part of the program can load the ent:

  a, _ = LoadAccountById(estore, 1)
  fmt.Printf("account #1: %v\n", a) // { _ver: "1", _id: "1", name: "Jane", ...

Notice that we started using Go's "fmt" package. If you are following along, import "fmt".

Since we specified unique on the email field, we can look up ents by email in addition to id:

  b, _ := LoadAccountByEmail(estore, "jane@example.com")
  fmt.Printf(`account with email "jane@example.com": %v\n`, b)
  // { _ver: "1", _id: "1", name: "Jane", ...

  _, err := LoadAccountByEmail(estore, "does@not.exist")
  fmt.Printf("error from lookup of non-existing email: %v\n", err)

If we just need to check if an ent exists, or we only need to know the id, we can use the Find... functions instead of the Load... functions:

  id, _ := FindAccountByEmail(estore, "jane@example.com")
  fmt.Printf(`id of account with email "jane@example.com": %v\n`, id) // 1

These functions were generated for us by entgen. The Find...ByFIELD and Load...ByFIELD functions performs a lookup on a secondary index ("email" in the example above.)

In our struct definition we declared that we wanted the kind field to be indexed, which means there are also functions for looking up accounts by kind. Indexes which are not unique, i.e. indexes declared with the "index" field tag rather than the "unique" tag, returns a list of ents. To make this example more interesting, let's create a few more ents to play with:

  (&Account{email: "robin@foo.com", kind: AccountMember}).Create(estore)
  (&Account{email: "thor@xy.z", kind: AccountAdmin}).Create(estore)
  (&Account{email: "alice@es.gr", kind: AccountRestricted}).Create(estore)

And let's try querying for different kinds of users:

  accounts, _ := LoadAccountByKind(estore, AccountMember, 0)
  fmt.Printf("'member' accounts: %+v\n", accounts)

  accounts, _ = LoadAccountByKind(estore, AccountAdmin, 0)
  fmt.Printf("'admin' accounts: %+v\n", accounts)

We should see "Jane" and "robin" listed for AccountMember and "thor" for AccountAdmin.

Non-unique indexes as we just explored does not imply any constraints on ents. But unique indexes do — it's kind of the whole point with a unique index :-) When we create or update an ent with a change to a unique index we may get an error in case there is a conflict. For example, let's try creating a new account that uses the same email address as Jane's account:

  err = (&Account{email: "jane@example.com"}).Create(estore)
  fmt.Printf("error (duplicate email): %v\n", err)
  // unique index conflict account.email with ent #1

The same would happen if we tried to update an account to use an already-used email value:

  a, _ = LoadAccountByEmail(estore, "robin@foo.com")
  a.SetEmail("jane@example.com")
  fmt.Printf("error (duplicate email): %v\n", a.Save())
  // unique index conflict account.email with ent #1

However if we change the email of Jane's account, we can reuse Jane's old email address:

  a, _ = LoadAccountById(estore, 1)
  a.SetEmail("jane.smith@foo.z")
  a.Save()

  a, _ = LoadAccountByEmail(estore, "robin@foo.com")
  a.SetEmail("jane@example.com")
  fmt.Printf("no error: %v\n", a.Save())

The ent system maintains these indexes automatically and updates them in a transactional manner: a Create or Save call either fully succeeds, including index changes, or has no effect at all. This promise is declared by the ent system but actually fulfilled by the particular storage used. Both of the storage implementations that comes with ent are fully transactional (mem and redis.)

Changes to ents are tracked with versioning. Every update to an ent increments its version. The version is used when updating an ent:

When we say "make X changes to ent Y of version Z" the storage...

  • Checks the current version against the expected version Z.

  • If the ent's version is Z in the storage then the changes are applied and its version is incremented to Z+1.

  • However if someone else made a change to the ent after we loaded it, the version in storage won't match Z and we get a ErrVersionConflict error.

Example of a version conflict:

  a1, _ := LoadAccountById(estore, 1)
  a2, _ := LoadAccountById(estore, 1)
  // make a change to copy a1 and save it
  a1.SetName("Jenn")
  a1.Save()
  // make a change to copy a2 and save it
  a2.SetName("Jeannie")
  fmt.Printf("version conflict error: %v\n", a2.Save())

To resolve a conflict we either need to discard our change to a2 or load the current version and reapply our changes. If simply replacing values is not what we want, we could load a second copy and merge our new values with the current ones. In this example we simply retry our edit on the most recent version:

  a2.Reload() // load msot current values from storage
  a2.SetName("Jeannie")
  fmt.Printf("save now works (no error): %v\n", a2.Save())

In some ways this approach resembles "compare and swap" memory operations:

atomically:
  if currentValue is expectedValue:
    setValue(newValue)

This versioning approach was inspired by CouchDB.

entgen

entgen is a program that parses go packages and generates ent code for all ent-enabled struct types.

You can either run entgen from source or go modules, or install it with go get github.com/rsms/ent/entgen. To install a specific version run go get github.com/rsms/ent/entgen@vX.X.X replacing the "X"es with the version you want.

Synopsis
Usage: entgen [options] [<srcdir> ...]
options:
  -debug
      Debug logging (implies -v)
  -entpkg string
      Import path of ent package (default "github.com/rsms/ent")
  -filter string
      Only process go struct types which name matches the provided
      regular expression
  -h, -help
      Show help and exit
  -nofmt
      Disable "gofmt" formatting of generated code
  -o string
      Filename of generated go code, relative to <srcdir>.
      Use "-" for stdout. (default "ents.gen.go")
  -v
      Verbose logging
  -version
      Print "entgen 0.1.0" and exit

Documentation

Index

Constants

View Source
const (
	EntIndexUnique = 1 << iota // a unique index entry points to exactly one ent
)
View Source
const (
	// ReprOmitEmpty causes empty, non-numeric fields to be excluded
	ReprOmitEmpty = ReprFlags(1 << iota)
)
View Source
const (
	// Reverse returns results in reverse order. Useful in combination with a limit.
	Reverse = LookupFlags(1 << iota)
)

Variables

View Source
var (
	ErrNoStorage       = errors.New("no ent storage")
	ErrNotFound        = errors.New("ent not found")
	ErrNotChanged      = errors.New("ent not changed")
	ErrVersionConflict = errors.New("version conflict")
	ErrUniqueConflict  = errors.New("unique index conflict")
	ErrDuplicateEnt    = errors.New("duplicate ent")
)
View Source
var (
	FieldNameVersion = "_ver"
	FieldNameId      = "_id"
)

Functions

func ClearFieldChanged

func ClearFieldChanged(e *EntBase, fieldIndex int)

ClearFieldChanged marks the field fieldIndex as not having and unsaved changes

func CreateEnt

func CreateEnt(e Ent, storage Storage) error

func DeleteAllEntsOfType added in v0.2.8

func DeleteAllEntsOfType(s Storage, prototype Ent) error

DeleteAllEntsOfType permanently DELETES ALL ents of the type of prototype

func DeleteEnt

func DeleteEnt(e Ent) error

func EntString added in v0.2.0

func EntString(e Ent) string

func FindIdByIndex added in v0.2.8

func FindIdByIndex(
	s Storage, entTypeName string, x *EntIndex, flags []LookupFlags,
	nfields int, keyEncoder func(Encoder),
) (uint64, error)

func FindIdByIndexKey added in v0.2.8

func FindIdByIndexKey(
	s Storage, entTypeName string, x *EntIndex, key []byte, flags []LookupFlags,
) (uint64, error)

func FindIdsByIndex added in v0.2.8

func FindIdsByIndex(
	s Storage, entTypeName string, x *EntIndex, limit int, flags []LookupFlags,
	nfields int, keyEncoder func(Encoder),
) ([]uint64, error)

func FindIdsByIndexKey added in v0.2.8

func FindIdsByIndexKey(
	s Storage, entTypeName string, x *EntIndex, key []byte, limit int, flags []LookupFlags,
) ([]uint64, error)

func GetFieldValue added in v0.2.11

func GetFieldValue(e Ent, fieldIndex int) reflect.Value

func IsFieldChanged

func IsFieldChanged(e *EntBase, fieldIndex int) bool

IsFieldChanged returns true if the field fieldIndex is marked as "having unsaved changes"

func JsonDecode

func JsonDecode(e Ent, data []byte) error

JsonEncode encodes the ent as JSON

func JsonDecodeEnt added in v0.2.0

func JsonDecodeEnt(e Ent, data []byte) (id, version uint64, err error)

func JsonDecodeEntPartial added in v0.2.0

func JsonDecodeEntPartial(e Ent, data []byte, fields FieldSet) (version uint64, err error)

JsonDecodeEntPartial is a utility function for decoding a partial ent. It calls e.EntDecodePartial and thus is limited to fields that participate in indexes.

func JsonEncode

func JsonEncode(e Ent, indent string) ([]byte, error)

JsonEncode encodes the ent as JSON

func JsonEncodeEnt added in v0.2.0

func JsonEncodeEnt(e Ent, id, version uint64, fields FieldSet, indent string) ([]byte, error)

func JsonEncodeUnsaved

func JsonEncodeUnsaved(e Ent, indent string) ([]byte, error)

JsonEncodeUnsaved encodes the ent as JSON, only including fields with unsaved changes

func LoadEntById

func LoadEntById(e Ent, storage Storage, id uint64) error

func LoadEntByIndex

func LoadEntByIndex(
	s Storage, e Ent, x *EntIndex, flags []LookupFlags,
	nfields int, keyEncoder func(Encoder),
) error

func LoadEntByIndexKey

func LoadEntByIndexKey(s Storage, e Ent, x *EntIndex, key []byte, flags []LookupFlags) error

func ReloadEnt

func ReloadEnt(e Ent) error

func Repr added in v0.2.0

func Repr(e Ent, fields FieldSet, flags ReprFlags) ([]byte, error)

Repr formats a human-readable representation of an ent. It only includes fields.

func SaveEnt

func SaveEnt(e Ent) error

func SetEntBaseFields added in v0.2.8

func SetEntBaseFields(e Ent, s Storage, id, version uint64, changes FieldSet)

SetEntBaseFields sets values of EntBase fields. This function is meant to be used by Storage implementation, called after a new ent has been loaded or created.

func SetEntBaseFieldsAfterLoad

func SetEntBaseFieldsAfterLoad(e Ent, s Storage, id, version uint64)

func SetFieldChanged

func SetFieldChanged(e *EntBase, fieldIndex int)

SetFieldChanged marks the field fieldIndex as "having unsaved changes"

Types

type Buffer

type Buffer []byte

Buffer is an extension to the byte array with functions for efficiently growing it, useful for constructing variably-sized byte arrays.

func NewBuffer

func NewBuffer(initcap int) Buffer

NewBuffer creates a buffer with some preallocated space. Note that you do not need to use this function. Simply using a default-initialized Buffer works just as well as it will be allocated on first grow() call.

func (Buffer) Bytes

func (b Buffer) Bytes() []byte

func (Buffer) DenseBytes

func (b Buffer) DenseBytes(densityThreshold float64) []byte

DenseBytes returns the receiver if the density (cap divided by len) is less than densityThreshold. Otherwise a perfectly dense copy is returned.

This is useful if you plan to keep a lot of buffers unmodified for a long period of time, where memory consumption might be a concern.

To always make a copy, provide a densityThreshold of 1.0 or lower.

func (*Buffer) Grow

func (b *Buffer) Grow(n int) int

Grow returns the index where bytes should be written and whether it succeeded.

func (*Buffer) Reserve

func (b *Buffer) Reserve(n int)

func (*Buffer) Reset

func (b *Buffer) Reset()

Reset truncates the buffer's length to zero, allowing it to be reused.

func (*Buffer) Write

func (b *Buffer) Write(data []byte) int

Write appends data to the buffer, returning the offset to the start of the appended data

func (*Buffer) WriteByte

func (b *Buffer) WriteByte(v byte)

func (*Buffer) WriteString

func (b *Buffer) WriteString(s string)

type Decoder

type Decoder interface {
	Err() error // returns the error state of the decoder

	// Key reads & returns the next key. Returns "" in case there are no more fields.
	Key() string

	ListHeader() int // decode a list header. Returns known size or -1 if unknown
	DictHeader() int // decode a dict header. Returns known size or -1 if unknown

	// More reports whether there is another element in the current list or dict being decoded.
	// Only used when ListHeader and DictHeader returns -1.
	More() bool

	Str() string  // decode a string field
	Blob() []byte // decode a byte array field
	Bool() bool
	Int(bitsize int) int64     // advisory size
	Uint(bitsize int) uint64   // advisory size
	Float(bitsize int) float64 // advisory size
	Discard()                  // read and discard any value
}

Decoder is the interface for ent field decoders. ent.JsonDecoder is an example of an implementation.

type Encoder

type Encoder interface {
	Err() error // returns the error state of the encoder

	BeginEnt(version uint64) // start encoding an ent
	EndEnt()                 // finalize encoding of an ent

	BeginList(length int) // start encoding a list of length
	EndList()             // end encoding a list

	BeginDict(length int) // start encoding a dictionary with length entries
	EndDict()             // end encoding of a dict

	Key(k string) // encode key for a field (value call should follow)

	Str(v string)                 // encode a string value
	Blob(v []byte)                // encode a raw-bytes value
	Int(v int64, bitsize int)     // advisory size
	Uint(v uint64, bitsize int)   // advisory size
	Float(v float64, bitsize int) // advisory size
	Bool(v bool)                  // encode a boolean value
}

Encoder is the interface for ent field encoders. ent.JsonEncoder is an example of an implementation.

type Ent

type Ent interface {
	Id() uint64
	Version() uint64
	HasUnsavedChanges() bool

	EntTypeName() string
	EntNew() Ent
	EntEncode(c Encoder, fields FieldSet)
	EntDecode(c Decoder) (id, version uint64)
	EntDecodePartial(c Decoder, fields FieldSet) (version uint64)
	EntIndexes() []EntIndex
	EntFields() Fields
}

func LoadEntsByIndex

func LoadEntsByIndex(
	s Storage, e Ent, x *EntIndex, limit int, flags []LookupFlags,
	nfields int, keyEncoder func(Encoder),
) ([]Ent, error)

func LoadEntsByIndexKey added in v0.2.8

func LoadEntsByIndexKey(
	s Storage, e Ent, x *EntIndex, key []byte, limit int, flags []LookupFlags,
) ([]Ent, error)

type EntBase

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

EntBase is the foundation for all ent types. Use it as the first embedded field in a struct to make the struct an ent.

func (*EntBase) ClearEntFieldChanged added in v0.2.11

func (e *EntBase) ClearEntFieldChanged(fieldIndex int)

func (*EntBase) EntDecode

func (e *EntBase) EntDecode(c Decoder) (id, version uint64)

func (*EntBase) EntEncode

func (e *EntBase) EntEncode(Encoder, uint64)

func (*EntBase) EntFields added in v0.2.0

func (e *EntBase) EntFields() Fields

func (*EntBase) EntIndexes

func (e *EntBase) EntIndexes() []EntIndex

func (*EntBase) EntPendingFields added in v0.2.11

func (e *EntBase) EntPendingFields() FieldSet

func (*EntBase) EntTypeName

func (e *EntBase) EntTypeName() string

these are just stubs; actual implementations generated by entgen

func (*EntBase) HasUnsavedChanges

func (e *EntBase) HasUnsavedChanges() bool

func (*EntBase) Id

func (e *EntBase) Id() uint64

func (*EntBase) IsEntFieldChanged added in v0.2.11

func (e *EntBase) IsEntFieldChanged(fieldIndex int) bool

func (*EntBase) SetEntFieldChanged added in v0.2.11

func (e *EntBase) SetEntFieldChanged(fieldIndex int)

func (*EntBase) Version

func (e *EntBase) Version() uint64

type EntIndex

type EntIndex struct {
	Name   string
	Fields FieldSet // bitmap of field indices which this index depends on
	Flags  EntIndexFlag
}

EntIndex describes a secondary index and are usually generated by entgen

func (EntIndex) IsUnique added in v0.2.0

func (x EntIndex) IsUnique() bool

IsUnique is true if a key in index maps to exactly one ent (i.e. keys are unique)

type EntIndexFlag

type EntIndexFlag int

EntIndexFlag describes properties of an EntIndex

type EntIterator added in v0.2.8

type EntIterator interface {
	// Next loads the next ent into e and returns true.
	// When the iterator has reached its end, this method returns false (does not modify e.)
	Next(e Ent) bool
	Err() error // returns non-nil if an error occured
}

type FieldSet added in v0.2.8

type FieldSet uint64

func FieldsWithEmptyValue added in v0.2.0

func FieldsWithEmptyValue(e Ent) FieldSet

FieldsWithEmptyValue returns a FieldSet of all non-numeric non-bool fields which has a zero value for its type.

func (FieldSet) Contains added in v0.2.8

func (f FieldSet) Contains(other FieldSet) bool

func (FieldSet) Has added in v0.2.8

func (f FieldSet) Has(fieldIndex int) bool

func (FieldSet) Len added in v0.2.8

func (f FieldSet) Len() int

func (FieldSet) Union added in v0.2.8

func (f FieldSet) Union(other FieldSet) FieldSet

func (FieldSet) With added in v0.2.8

func (f FieldSet) With(fieldIndex int) FieldSet

func (FieldSet) Without added in v0.2.8

func (f FieldSet) Without(fieldIndex int) FieldSet

type Fields added in v0.2.0

type Fields struct {
	Names    []string // names of fields, ordered by field index
	FieldSet          // bitmap with all fields set
}

Fields describes fields of an ent. Available via TYPE.EntFields()

type Id

type Id uint64

type IdIterator added in v0.2.8

type IdIterator interface {
	// Next reads the next id. Returns false when the iterator has reached its end.
	Next(id *uint64) bool
	Err() error // returns non-nil if an error occured
}

type IdSet added in v0.2.0

type IdSet []uint64

IdSet is a list of integers which are treated as a set

func ParseIdSet added in v0.2.0

func ParseIdSet(data []byte) IdSet

func (*IdSet) Add added in v0.2.0

func (s *IdSet) Add(id uint64)

func (*IdSet) Del added in v0.2.0

func (s *IdSet) Del(id uint64)

func (IdSet) Encode added in v0.2.0

func (s IdSet) Encode() []byte

func (IdSet) Has added in v0.2.0

func (s IdSet) Has(id uint64) bool

func (IdSet) Sort added in v0.2.0

func (s IdSet) Sort()

type IndexConflictErr added in v0.2.6

type IndexConflictErr struct {
	Underlying  error  // i.e. ErrUniqueConflict
	EntTypeName string // typename of subject ent (== TYPE.EntTypeName())
	IndexName   string // name of subject index
}

IndexConflictErr is returned when a Create or Save call fails because there is an index conflict.

func (*IndexConflictErr) Error added in v0.2.6

func (e *IndexConflictErr) Error() string

func (*IndexConflictErr) Unwrap added in v0.2.6

func (e *IndexConflictErr) Unwrap() error

type IndexGetter

type IndexGetter = func(entTypeName, indexName, key string) ([]uint64, error)

IndexGetter is used to look up an entry in an index

type IndexKeyEncoder

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

IndexKeyEncoder is an implementation of the Encoder interface, used to encode index keys

func (*IndexKeyEncoder) BeginDict

func (c *IndexKeyEncoder) BeginDict(length int)

func (*IndexKeyEncoder) BeginEnt

func (c *IndexKeyEncoder) BeginEnt(version uint64)

func (*IndexKeyEncoder) BeginList

func (c *IndexKeyEncoder) BeginList(length int)

func (*IndexKeyEncoder) Blob

func (c *IndexKeyEncoder) Blob(v []byte)

func (*IndexKeyEncoder) Bool

func (c *IndexKeyEncoder) Bool(v bool)

func (*IndexKeyEncoder) EncodeKey

func (c *IndexKeyEncoder) EncodeKey(e Ent, fields FieldSet) ([]byte, error)

func (*IndexKeyEncoder) EndDict

func (c *IndexKeyEncoder) EndDict()

func (*IndexKeyEncoder) EndEnt

func (c *IndexKeyEncoder) EndEnt()

func (*IndexKeyEncoder) EndList

func (c *IndexKeyEncoder) EndList()

func (*IndexKeyEncoder) Err

func (c *IndexKeyEncoder) Err() error

func (*IndexKeyEncoder) Float

func (c *IndexKeyEncoder) Float(v float64, bitsize int)

func (*IndexKeyEncoder) Int

func (c *IndexKeyEncoder) Int(v int64, bitsize int)

func (*IndexKeyEncoder) Key

func (c *IndexKeyEncoder) Key(k string)

func (*IndexKeyEncoder) Reset

func (c *IndexKeyEncoder) Reset(nfields int)

func (*IndexKeyEncoder) Str

func (c *IndexKeyEncoder) Str(v string)

func (*IndexKeyEncoder) Uint

func (c *IndexKeyEncoder) Uint(v uint64, bitsize int)

type JsonDecoder

type JsonDecoder struct {
	json.Reader
}

JsonDecoder is an implementation of the Decoder interface

func NewJsonDecoder

func NewJsonDecoder(data []byte) *JsonDecoder

func (*JsonDecoder) DictHeader

func (c *JsonDecoder) DictHeader() int

func (*JsonDecoder) ListHeader

func (c *JsonDecoder) ListHeader() int

type JsonEncoder

type JsonEncoder struct {
	json.Builder      // Note: set Builder.Indent to enable pretty-printing
	BareKeys     bool // when true, don't wrap keys in "..."
}

JsonEncoder is an implementation of the Encoder interface

func (*JsonEncoder) BeginDict

func (c *JsonEncoder) BeginDict(length int)

func (*JsonEncoder) BeginEnt

func (c *JsonEncoder) BeginEnt(version uint64)

func (*JsonEncoder) BeginList

func (c *JsonEncoder) BeginList(length int)

func (*JsonEncoder) EndDict

func (c *JsonEncoder) EndDict()

func (*JsonEncoder) EndEnt

func (c *JsonEncoder) EndEnt()

func (*JsonEncoder) EndList

func (c *JsonEncoder) EndList()

func (*JsonEncoder) Err

func (c *JsonEncoder) Err() error

func (*JsonEncoder) Key added in v0.2.0

func (e *JsonEncoder) Key(k string)

type JsonError

type JsonError struct {
	Underlying error
}

func (*JsonError) Error

func (e *JsonError) Error() string

func (*JsonError) Unwrap

func (e *JsonError) Unwrap() error

type LookupFlags added in v0.2.8

type LookupFlags int

LookupFlags describe options for lookup

type ReprFlags added in v0.2.0

type ReprFlags int

ReprFlags are changes behavior of Repr

type Storage

type Storage interface {
	Create(e Ent, fields FieldSet) (id uint64, err error)
	Save(e Ent, fields FieldSet) (version uint64, err error)
	LoadById(e Ent, id uint64) (version uint64, err error)
	LoadByIndex(e Ent, x *EntIndex, key []byte, limit int, fl LookupFlags) ([]Ent, error)
	FindByIndex(entType string, x *EntIndex, key []byte, limit int, fl LookupFlags) ([]uint64, error)
	IterateIds(entType string) IdIterator
	IterateEnts(proto Ent) EntIterator
	Delete(e Ent, id uint64) error
}

Storage is the interface for persistent storage of ents

func GetStorage added in v0.2.9

func GetStorage(e Ent) Storage

type StorageIndexEdit

type StorageIndexEdit struct {
	Index *EntIndex
	Key   string
	Value []uint64

	// IsCleanup is true when the edit represents removing an id for an index entry.
	// A Storage implementation can choose to perform these edits independently from or outside of
	// the logical transaction of modifying an ent.
	IsCleanup bool
}

StorageIndexEdit represents a modification to an index

func ComputeIndexEdits added in v0.2.0

func ComputeIndexEdits(
	indexGet IndexGetter,
	prevEnt, nextEnt Ent,
	id uint64,
	changedFields FieldSet,
) ([]StorageIndexEdit, error)

ComputeIndexEdits calculates changes to secondary indexes.

When a new ent is created, prevEnt should be nil. When an ent is deleted, nextEnt should be nil. When an ent is modified, both nextEnt and prevEnt should be provided.

When a new ent is modified or deleted, prevEnt should be the "current" version of the ent. In these cases prevEnt's field values are used to determine what index entries needs to be cleaned up.

prevEnt only needs to loaded fields which indices are the union of all the prevEnt.EntIndexes() Fields values. Example:

var fieldsToLoad uint64
for _, x := range nextEnt.EntIndexes() {
  fieldsToLoad |= x.Fields
}
DecodePartialEnt(prevEnt, fieldsToLoad)

Note: StorageIndexEdits with IsCleanup=true always comes before a StorageIndexEdit for the same key that is not a cleanup. This makes it possible to perform patches using a single loop, e.g:

for _, ed := range indexEdits {
  key := indexKey(ed.Index.Name, ed.Key)
  if ed.IsCleanup {
    db.Delete(key)
  } else {
    db.Set(key, id)
  }
}

indexGet may be nil in which case it is assumed that calling storage handles sets of ids. For example, ent/redis.EntStorage handles ids of non-unique indexes manually and thus provides nil for indexGet, while ent/mem.EntStorage does not handle id sets itself and instead provides a function, for reading its current state, as indexGet.

type VersionConflictErr

type VersionConflictErr struct {
	Underlying      error // always ErrVersionConflict
	ExpectedVersion uint64
	ActualVersion   uint64
}

VersionConflictErr is returned when a Save call fails because the ent has changed by someone else since it was loaded.

func NewVersionConflictErr

func NewVersionConflictErr(expectedVersion, actualVersion uint64) *VersionConflictErr

func (*VersionConflictErr) Error

func (e *VersionConflictErr) Error() string

func (*VersionConflictErr) Unwrap

func (e *VersionConflictErr) Unwrap() error

Directories

Path Synopsis
examples
mem
Code generated by entgen.
Code generated by entgen.
redis
Code generated by entgen.
Code generated by entgen.
tutorial
Code generated by entgen.
Code generated by entgen.

Jump to

Keyboard shortcuts

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