212 lines
5.6 KiB
Go
212 lines
5.6 KiB
Go
package orm
|
|
|
|
import (
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/x/group/errors"
|
|
)
|
|
|
|
// IndexerFunc creates one or multiple index keys for the source object.
|
|
type IndexerFunc func(value interface{}) ([]interface{}, error)
|
|
|
|
// IndexerFunc creates exactly one index key for the source object.
|
|
type UniqueIndexerFunc func(value interface{}) (interface{}, error)
|
|
|
|
// Indexer manages the persistence of an Index based on searchable keys and operations.
|
|
type Indexer struct {
|
|
indexerFunc IndexerFunc
|
|
addFunc func(store sdk.KVStore, secondaryIndexKey interface{}, rowID RowID) error
|
|
}
|
|
|
|
// NewIndexer returns an indexer that supports multiple reference keys for an entity.
|
|
func NewIndexer(indexerFunc IndexerFunc) (*Indexer, error) {
|
|
if indexerFunc == nil {
|
|
return nil, errors.ErrORMInvalidArgument.Wrap("Indexer func must not be nil")
|
|
}
|
|
return &Indexer{
|
|
indexerFunc: pruneEmptyKeys(indexerFunc),
|
|
addFunc: multiKeyAddFunc,
|
|
}, nil
|
|
}
|
|
|
|
// NewUniqueIndexer returns an indexer that requires exactly one reference keys for an entity.
|
|
func NewUniqueIndexer(f UniqueIndexerFunc) (*Indexer, error) {
|
|
if f == nil {
|
|
return nil, errors.ErrORMInvalidArgument.Wrap("Indexer func must not be nil")
|
|
}
|
|
adaptor := func(indexerFunc UniqueIndexerFunc) IndexerFunc {
|
|
return func(v interface{}) ([]interface{}, error) {
|
|
k, err := indexerFunc(v)
|
|
return []interface{}{k}, err
|
|
}
|
|
}
|
|
idx, err := NewIndexer(adaptor(f))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
idx.addFunc = uniqueKeysAddFunc
|
|
return idx, nil
|
|
}
|
|
|
|
// IndexerFunc returns the indexer IndexerFunc,
|
|
// ensuring it has been prune from empty keys.
|
|
func (i Indexer) IndexerFunc() IndexerFunc {
|
|
return i.indexerFunc
|
|
}
|
|
|
|
// OnCreate persists the secondary index entries for the new object.
|
|
func (i Indexer) OnCreate(store sdk.KVStore, rowID RowID, value interface{}) error {
|
|
secondaryIndexKeys, err := i.indexerFunc(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, secondaryIndexKey := range secondaryIndexKeys {
|
|
if err := i.addFunc(store, secondaryIndexKey, []byte(rowID)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OnDelete removes the secondary index entries for the deleted object.
|
|
func (i Indexer) OnDelete(store sdk.KVStore, rowID RowID, value interface{}) error {
|
|
secondaryIndexKeys, err := i.indexerFunc(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, secondaryIndexKey := range secondaryIndexKeys {
|
|
indexKey, err := buildKeyFromParts([]interface{}{secondaryIndexKey, []byte(rowID)})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
store.Delete(indexKey)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OnUpdate rebuilds the secondary index entries for the updated object.
|
|
func (i Indexer) OnUpdate(store sdk.KVStore, rowID RowID, newValue, oldValue interface{}) error {
|
|
oldSecIdxKeys, err := i.indexerFunc(oldValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newSecIdxKeys, err := i.indexerFunc(newValue)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
oldKeys, err := difference(oldSecIdxKeys, newSecIdxKeys)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, oldIdxKey := range oldKeys {
|
|
indexKey, err := buildKeyFromParts([]interface{}{oldIdxKey, []byte(rowID)})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
store.Delete(indexKey)
|
|
}
|
|
newKeys, err := difference(newSecIdxKeys, oldSecIdxKeys)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, newIdxKey := range newKeys {
|
|
if err := i.addFunc(store, newIdxKey, rowID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// uniqueKeysAddFunc enforces keys to be unique
|
|
func uniqueKeysAddFunc(store sdk.KVStore, secondaryIndexKey interface{}, rowID RowID) error {
|
|
secondaryIndexKeyBytes, err := keyPartBytes(secondaryIndexKey, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(secondaryIndexKeyBytes) == 0 {
|
|
return sdkerrors.Wrap(errors.ErrORMInvalidArgument, "empty index key")
|
|
}
|
|
|
|
it := store.Iterator(PrefixRange(secondaryIndexKeyBytes))
|
|
defer it.Close()
|
|
if it.Valid() {
|
|
return errors.ErrORMUniqueConstraint
|
|
}
|
|
|
|
indexKey, err := buildKeyFromParts([]interface{}{secondaryIndexKey, []byte(rowID)})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
store.Set(indexKey, []byte{})
|
|
return nil
|
|
}
|
|
|
|
// multiKeyAddFunc allows multiple entries for a key
|
|
func multiKeyAddFunc(store sdk.KVStore, secondaryIndexKey interface{}, rowID RowID) error {
|
|
secondaryIndexKeyBytes, err := keyPartBytes(secondaryIndexKey, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(secondaryIndexKeyBytes) == 0 {
|
|
return sdkerrors.Wrap(errors.ErrORMInvalidArgument, "empty index key")
|
|
}
|
|
|
|
encodedKey, err := buildKeyFromParts([]interface{}{secondaryIndexKey, []byte(rowID)})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(encodedKey) == 0 {
|
|
return sdkerrors.Wrap(errors.ErrORMInvalidArgument, "empty index key")
|
|
}
|
|
|
|
store.Set(encodedKey, []byte{})
|
|
return nil
|
|
}
|
|
|
|
// difference returns the list of elements that are in a but not in b.
|
|
func difference(a []interface{}, b []interface{}) ([]interface{}, error) {
|
|
set := make(map[interface{}]struct{}, len(b))
|
|
for _, v := range b {
|
|
bt, err := keyPartBytes(v, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
set[string(bt)] = struct{}{}
|
|
}
|
|
var result []interface{}
|
|
for _, v := range a {
|
|
bt, err := keyPartBytes(v, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, ok := set[string(bt)]; !ok {
|
|
result = append(result, v)
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// pruneEmptyKeys drops any empty key from IndexerFunc f returned
|
|
func pruneEmptyKeys(f IndexerFunc) IndexerFunc {
|
|
return func(v interface{}) ([]interface{}, error) {
|
|
keys, err := f(v)
|
|
if err != nil || keys == nil {
|
|
return keys, err
|
|
}
|
|
r := make([]interface{}, 0, len(keys))
|
|
for i := range keys {
|
|
key, err := keyPartBytes(keys[i], true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(key) != 0 {
|
|
r = append(r, keys[i])
|
|
}
|
|
}
|
|
return r, nil
|
|
}
|
|
}
|