167 lines
3.0 KiB
Go
167 lines
3.0 KiB
Go
package storm
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/asdine/storm/index"
|
|
"github.com/boltdb/bolt"
|
|
"github.com/fatih/structs"
|
|
)
|
|
|
|
// Storm tags
|
|
const (
|
|
tagID = "id"
|
|
tagIdx = "index"
|
|
tagUniqueIdx = "unique"
|
|
tagInline = "inline"
|
|
indexPrefix = "__storm_index_"
|
|
)
|
|
|
|
type indexInfo struct {
|
|
Type string
|
|
Field *structs.Field
|
|
}
|
|
|
|
// modelInfo is a structure gathering all the relevant informations about a model
|
|
type modelInfo struct {
|
|
Name string
|
|
Indexes map[string]indexInfo
|
|
ID identInfo
|
|
data interface{}
|
|
}
|
|
|
|
func (m *modelInfo) AddIndex(f *structs.Field, indexType string, override bool) {
|
|
fieldName := f.Name()
|
|
if _, ok := m.Indexes[fieldName]; !ok || override {
|
|
m.Indexes[fieldName] = indexInfo{
|
|
Type: indexType,
|
|
Field: f,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *modelInfo) AllByType(indexType string) []indexInfo {
|
|
var idx []indexInfo
|
|
for k := range m.Indexes {
|
|
if m.Indexes[k].Type == indexType {
|
|
idx = append(idx, m.Indexes[k])
|
|
}
|
|
}
|
|
|
|
return idx
|
|
}
|
|
|
|
func extract(data interface{}, mi ...*modelInfo) (*modelInfo, error) {
|
|
s := structs.New(data)
|
|
fields := s.Fields()
|
|
|
|
var child bool
|
|
|
|
var m *modelInfo
|
|
if len(mi) > 0 {
|
|
m = mi[0]
|
|
child = true
|
|
} else {
|
|
m = &modelInfo{}
|
|
m.Indexes = make(map[string]indexInfo)
|
|
m.data = data
|
|
}
|
|
|
|
if m.Name == "" {
|
|
m.Name = s.Name()
|
|
}
|
|
|
|
for _, f := range fields {
|
|
if !f.IsExported() {
|
|
continue
|
|
}
|
|
|
|
err := extractField(f, m, child)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// ID field or tag detected
|
|
if m.ID.Field != nil {
|
|
if m.ID.Field.IsZero() {
|
|
m.ID.IsZero = true
|
|
} else {
|
|
m.ID.Value = m.ID.Field.Value()
|
|
}
|
|
}
|
|
|
|
if child {
|
|
return m, nil
|
|
}
|
|
|
|
if m.ID.Field == nil {
|
|
return nil, ErrNoID
|
|
}
|
|
|
|
if m.Name == "" {
|
|
return nil, ErrNoName
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func extractField(f *structs.Field, m *modelInfo, isChild bool) error {
|
|
tag := f.Tag("storm")
|
|
if tag != "" {
|
|
switch tag {
|
|
case "id":
|
|
m.ID.Field = f
|
|
case tagUniqueIdx, tagIdx:
|
|
m.AddIndex(f, tag, !isChild)
|
|
case tagInline:
|
|
if structs.IsStruct(f.Value()) {
|
|
_, err := extract(f.Value(), m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
default:
|
|
return ErrUnknownTag
|
|
}
|
|
}
|
|
|
|
// the field is named ID and no ID field has been detected before
|
|
if f.Name() == "ID" && m.ID.Field == nil {
|
|
m.ID.Field = f
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Prefill the most requested informations
|
|
type identInfo struct {
|
|
Field *structs.Field
|
|
IsZero bool
|
|
Value interface{}
|
|
}
|
|
|
|
func (i *identInfo) Type() reflect.Type {
|
|
return reflect.TypeOf(i.Field.Value())
|
|
}
|
|
|
|
func (i *identInfo) IsOfIntegerFamily() bool {
|
|
return i.Field != nil && i.Field.Kind() >= reflect.Int && i.Field.Kind() <= reflect.Uint64
|
|
}
|
|
|
|
func getIndex(bucket *bolt.Bucket, idxKind string, fieldName string) (index.Index, error) {
|
|
var idx index.Index
|
|
var err error
|
|
|
|
switch idxKind {
|
|
case tagUniqueIdx:
|
|
idx, err = index.NewUniqueIndex(bucket, []byte(indexPrefix+fieldName))
|
|
case tagIdx:
|
|
idx, err = index.NewListIndex(bucket, []byte(indexPrefix+fieldName))
|
|
default:
|
|
err = ErrIdxNotFound
|
|
}
|
|
|
|
return idx, err
|
|
}
|