Save and find use Index

This commit is contained in:
Asdine El Hrychy
2016-03-09 19:24:30 +01:00
parent 9535e45031
commit a8cacd81cb
5 changed files with 82 additions and 101 deletions

View File

@@ -9,4 +9,9 @@ var (
ErrBadType = errors.New("provided data must be a struct or a pointer to struct")
ErrAlreadyExists = errors.New("already exists")
ErrNilParam = errors.New("param must not be nil")
ErrBadIndexType = errors.New("bad index type")
ErrBadTarget = errors.New("provided target must be a pointer to a slice")
ErrNoName = errors.New("provided target must have a name")
ErrIndexNotFound = errors.New("index not found")
ErrNotFound = errors.New("not found")
)

60
find.go
View File

@@ -2,7 +2,6 @@ package storm
import (
"encoding/json"
"errors"
"fmt"
"reflect"
@@ -11,11 +10,11 @@ import (
)
// Find returns one or more records by the specified index
func (s *DB) Find(index string, value interface{}, to interface{}) error {
func (s *DB) Find(fieldName string, value interface{}, to interface{}) error {
ref := reflect.ValueOf(to)
if ref.Kind() != reflect.Ptr || reflect.Indirect(ref).Kind() != reflect.Slice {
return errors.New("provided target must be a pointer to a slice")
return ErrBadTarget
}
typ := reflect.Indirect(ref).Type().Elem()
@@ -24,17 +23,17 @@ func (s *DB) Find(index string, value interface{}, to interface{}) error {
d := structs.New(newElem.Interface())
bucketName := d.Name()
if bucketName == "" {
return errors.New("provided target must have a name")
return ErrNoName
}
field, ok := d.FieldOk(index)
field, ok := d.FieldOk(fieldName)
if !ok {
return fmt.Errorf("field %s not found", index)
return fmt.Errorf("field %s not found", fieldName)
}
tag := field.Tag("storm")
if tag == "" {
return fmt.Errorf("index %s not found", index)
return fmt.Errorf("index %s not found", fieldName)
}
return s.Bolt.View(func(tx *bolt.Tx) error {
@@ -43,9 +42,22 @@ func (s *DB) Find(index string, value interface{}, to interface{}) error {
return fmt.Errorf("bucket %s not found", bucketName)
}
idx := bucket.Bucket([]byte(index))
if idx == nil {
return fmt.Errorf("index %s not found", index)
var idx Index
var err error
switch tag {
case "unique":
idx, err = NewUniqueIndex(bucket, []byte(fieldName))
case "index":
idx, err = NewListIndex(bucket, []byte(fieldName))
default:
err = ErrBadIndexType
}
if err != nil {
if err == ErrIndexNotFound {
return ErrNotFound
}
return err
}
val, err := toBytes(value)
@@ -53,34 +65,20 @@ func (s *DB) Find(index string, value interface{}, to interface{}) error {
return err
}
raw := idx.Get(val)
if raw == nil {
return errors.New("not found")
}
var list [][]byte
if tag == "unique" {
list = append(list, raw)
} else if tag == "index" {
err = json.Unmarshal(raw, &list)
if err != nil {
return err
list, err := idx.All(val)
if err != nil {
if err == ErrIndexNotFound {
return ErrNotFound
}
if list == nil || len(list) == 0 {
return errors.New("not found")
}
} else {
return fmt.Errorf("unsupported struct tag %s", tag)
return err
}
results := reflect.MakeSlice(reflect.Indirect(ref).Type(), len(list), len(list))
for i := range list {
raw = bucket.Get(list[i])
raw := bucket.Get(list[i])
if raw == nil {
return errors.New("not found")
return ErrNotFound
}
err = json.Unmarshal(raw, results.Index(i).Addr().Interface())

View File

@@ -22,9 +22,16 @@ type Index interface {
// NewUniqueIndex loads a UniqueIndex
func NewUniqueIndex(parent *bolt.Bucket, indexName []byte) (*UniqueIndex, error) {
b, err := parent.CreateBucketIfNotExists(indexName)
if err != nil {
return nil, err
var err error
b := parent.Bucket(indexName)
if b == nil {
if !parent.Writable() {
return nil, ErrIndexNotFound
}
b, err = parent.CreateBucket(indexName)
if err != nil {
return nil, err
}
}
return &UniqueIndex{
@@ -115,9 +122,16 @@ func (idx *UniqueIndex) first() []byte {
// NewListIndex loads a ListIndex
func NewListIndex(parent *bolt.Bucket, indexName []byte) (*ListIndex, error) {
b, err := parent.CreateBucketIfNotExists(indexName)
if err != nil {
return nil, err
var err error
b := parent.Bucket(indexName)
if b == nil {
if !parent.Writable() {
return nil, ErrIndexNotFound
}
b, err = parent.CreateBucket(indexName)
if err != nil {
return nil, err
}
}
ids, err := NewUniqueIndex(b, []byte("storm__ids"))

63
save.go
View File

@@ -13,69 +13,58 @@ func (s *DB) Save(data interface{}) error {
return ErrBadType
}
t, err := extractTags(data)
info, err := extract(data)
if err != nil {
return err
}
if t.ZeroID {
return ErrZeroID
}
if t.ID == nil {
if info.ID == nil {
return ErrNoID
}
id, err := toBytes(t.ID)
if info.ID.IsZero() {
return ErrZeroID
}
id, err := toBytes(info.ID.Value())
if err != nil {
return err
}
err = s.Bolt.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte(t.Name))
bucket, err := tx.CreateBucketIfNotExists([]byte(info.Name))
if err != nil {
return err
}
if len(t.Uniques) > 0 {
err = s.deleteOldIndexes(bucket, id, t.Uniques, true)
var idx Index
for fieldName, idxInfo := range info.Indexes {
switch idxInfo.Type {
case "unique":
idx, err = NewUniqueIndex(bucket, []byte(fieldName))
case "index":
idx, err = NewListIndex(bucket, []byte(fieldName))
default:
err = ErrBadIndexType
}
if err != nil {
return err
}
}
if len(t.Indexes) > 0 {
err = s.deleteOldIndexes(bucket, id, t.Indexes, false)
err = idx.RemoveID(id)
if err != nil {
return err
}
}
if t.Uniques != nil {
for _, field := range t.Uniques {
key, err := toBytes(field.Value())
if err != nil {
return err
}
err = s.addToUniqueIndex([]byte(field.Name()), id, key, bucket)
if err != nil {
return err
}
value, err := toBytes(idxInfo.Field.Value())
if err != nil {
return err
}
}
if t.Indexes != nil {
for _, field := range t.Indexes {
key, err := toBytes(field.Value())
if err != nil {
return err
}
err = s.addToListIndex([]byte(field.Name()), id, key, bucket)
if err != nil {
return err
}
err = idx.Add(value, id)
if err != nil {
return err
}
}

View File

@@ -17,6 +17,7 @@ func TestSave(t *testing.T) {
db, _ := Open(filepath.Join(dir, "storm.db"))
err := db.Save(&SimpleUser{ID: 10, Name: "John"})
assert.NoError(t, err)
err = db.Save(&SimpleUser{Name: "John"})
assert.Error(t, err)
@@ -117,32 +118,6 @@ func TestSaveIndex(t *testing.T) {
err = db.Save(&u2)
assert.NoError(t, err)
db.Bolt.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("IndexedNameUser"))
listBucket := bucket.Bucket([]byte("Name"))
assert.NotNil(t, listBucket)
raw := listBucket.Get([]byte("John"))
assert.NotNil(t, raw)
var list [][]byte
err = json.Unmarshal(raw, &list)
assert.NoError(t, err)
assert.Len(t, list, 2)
id1, err := toBytes(u1.ID)
assert.NoError(t, err)
id2, err := toBytes(u2.ID)
assert.NoError(t, err)
assert.Equal(t, id1, list[0])
assert.Equal(t, id2, list[1])
return nil
})
name1 := "Jake"
name2 := "Jane"
name3 := "James"
@@ -170,7 +145,7 @@ func TestSaveIndex(t *testing.T) {
err = db.Find("Name", name3, &users)
assert.Error(t, err)
assert.EqualError(t, err, "not found")
assert.Equal(t, ErrNotFound, err)
err = db.Save(nil)
assert.Error(t, err)