mirror of
https://github.com/asdine/storm.git
synced 2025-10-05 14:56:58 +08:00
Multi tag support everywhere
This commit is contained in:
120
bench_test.go
120
bench_test.go
@@ -6,6 +6,66 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func BenchmarkFindWithIndex(b *testing.B) {
|
||||||
|
db, cleanup := createDB(b, AutoIncrement())
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
var users []User
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
var w User
|
||||||
|
|
||||||
|
if i%2 == 0 {
|
||||||
|
w.Name = "John"
|
||||||
|
w.Group = "Staff"
|
||||||
|
} else {
|
||||||
|
w.Name = "Jack"
|
||||||
|
w.Group = "Admin"
|
||||||
|
}
|
||||||
|
err := db.Save(&w)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
err := db.Find("Name", "John", &users)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFindWithoutIndex(b *testing.B) {
|
||||||
|
db, cleanup := createDB(b, AutoIncrement())
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
var users []User
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
var w User
|
||||||
|
|
||||||
|
if i%2 == 0 {
|
||||||
|
w.Name = "John"
|
||||||
|
w.Group = "Staff"
|
||||||
|
} else {
|
||||||
|
w.Name = "Jack"
|
||||||
|
w.Group = "Admin"
|
||||||
|
}
|
||||||
|
err := db.Save(&w)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
err := db.Find("Group", "Staff", &users)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkOneWithIndex(b *testing.B) {
|
func BenchmarkOneWithIndex(b *testing.B) {
|
||||||
db, cleanup := createDB(b, AutoIncrement())
|
db, cleanup := createDB(b, AutoIncrement())
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
@@ -81,66 +141,6 @@ func BenchmarkOneWithoutIndex(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkFindWithIndex(b *testing.B) {
|
|
||||||
db, cleanup := createDB(b, AutoIncrement())
|
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
var users []User
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
var w User
|
|
||||||
|
|
||||||
if i%2 == 0 {
|
|
||||||
w.Name = "John"
|
|
||||||
w.Group = "Staff"
|
|
||||||
} else {
|
|
||||||
w.Name = "Jack"
|
|
||||||
w.Group = "Admin"
|
|
||||||
}
|
|
||||||
err := db.Save(&w)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
err := db.Find("Name", "John", &users)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkFindWithoutIndex(b *testing.B) {
|
|
||||||
db, cleanup := createDB(b, AutoIncrement())
|
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
var users []User
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
var w User
|
|
||||||
|
|
||||||
if i%2 == 0 {
|
|
||||||
w.Name = "John"
|
|
||||||
w.Group = "Staff"
|
|
||||||
} else {
|
|
||||||
w.Name = "Jack"
|
|
||||||
w.Group = "Admin"
|
|
||||||
}
|
|
||||||
err := db.Save(&w)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
err := db.Find("Group", "Staff", &users)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSave(b *testing.B) {
|
func BenchmarkSave(b *testing.B) {
|
||||||
db, cleanup := createDB(b, AutoIncrement())
|
db, cleanup := createDB(b, AutoIncrement())
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
30
extract.go
30
extract.go
@@ -1,6 +1,7 @@
|
|||||||
package storm
|
package storm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -76,15 +77,6 @@ func extract(s *reflect.Value, mi ...*structConfig) (*structConfig, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID field or tag detected
|
|
||||||
if m.ID != nil {
|
|
||||||
zero := reflect.Zero(m.ID.Value.Type()).Interface()
|
|
||||||
current := m.ID.Value.Interface()
|
|
||||||
if reflect.DeepEqual(current, zero) {
|
|
||||||
m.ID.IsZero = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if child {
|
if child {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
@@ -157,8 +149,10 @@ func extractField(value *reflect.Value, field *reflect.StructField, m *structCon
|
|||||||
Name: field.Name,
|
Name: field.Name,
|
||||||
IsZero: isZero(value),
|
IsZero: isZero(value),
|
||||||
IsInteger: isInteger(value),
|
IsInteger: isInteger(value),
|
||||||
|
IsID: true,
|
||||||
Value: value,
|
Value: value,
|
||||||
}
|
}
|
||||||
|
m.Fields[field.Name] = f
|
||||||
}
|
}
|
||||||
m.ID = f
|
m.ID = f
|
||||||
}
|
}
|
||||||
@@ -166,6 +160,24 @@ func extractField(value *reflect.Value, field *reflect.StructField, m *structCon
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractSingleField(ref *reflect.Value, fieldName string) (*structConfig, error) {
|
||||||
|
var cfg structConfig
|
||||||
|
cfg.Fields = make(map[string]*fieldConfig)
|
||||||
|
|
||||||
|
f, ok := ref.Type().FieldByName(fieldName)
|
||||||
|
if !ok || f.PkgPath != "" {
|
||||||
|
return nil, fmt.Errorf("field %s not found", fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := ref.FieldByName(fieldName)
|
||||||
|
err := extractField(&v, &f, &cfg, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getIndex(bucket *bolt.Bucket, idxKind string, fieldName string) (index.Index, error) {
|
func getIndex(bucket *bolt.Bucket, idxKind string, fieldName string) (index.Index, error) {
|
||||||
var idx index.Index
|
var idx index.Index
|
||||||
var err error
|
var err error
|
||||||
|
@@ -72,3 +72,28 @@ func TestExtractInlineWithIndex(t *testing.T) {
|
|||||||
assert.Len(t, allByType(infos, "index"), 3)
|
assert.Len(t, allByType(infos, "index"), 3)
|
||||||
assert.Len(t, allByType(infos, "unique"), 2)
|
assert.Len(t, allByType(infos, "unique"), 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExtractMultipleTags(t *testing.T) {
|
||||||
|
type User struct {
|
||||||
|
ID uint64 `storm:"id,increment"`
|
||||||
|
Age uint16 `storm:"index,increment"`
|
||||||
|
unexportedField int32 `storm:"index,increment"`
|
||||||
|
Pos string `storm:"unique,increment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
s := User{}
|
||||||
|
r := reflect.ValueOf(&s)
|
||||||
|
infos, err := extract(&r)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, infos)
|
||||||
|
assert.NotNil(t, infos.ID)
|
||||||
|
assert.Equal(t, "User", infos.Name)
|
||||||
|
assert.Len(t, allByType(infos, "index"), 1)
|
||||||
|
assert.Len(t, allByType(infos, "unique"), 1)
|
||||||
|
assert.True(t, infos.Fields["Age"].Increment)
|
||||||
|
assert.Equal(t, "index", infos.Fields["Age"].Index)
|
||||||
|
assert.False(t, infos.Fields["Age"].IsID)
|
||||||
|
assert.True(t, infos.Fields["Age"].IsInteger)
|
||||||
|
assert.True(t, infos.Fields["Age"].IsZero)
|
||||||
|
assert.NotNil(t, infos.Fields["Age"].Value)
|
||||||
|
}
|
||||||
|
71
finder.go
71
finder.go
@@ -1,9 +1,7 @@
|
|||||||
package storm
|
package storm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/asdine/storm/index"
|
"github.com/asdine/storm/index"
|
||||||
"github.com/asdine/storm/q"
|
"github.com/asdine/storm/q"
|
||||||
@@ -51,15 +49,14 @@ func (n *node) One(fieldName string, value interface{}, to interface{}) error {
|
|||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := reflect.Indirect(sink.ref).Type()
|
ref := reflect.Indirect(sink.ref)
|
||||||
|
cfg, err := extractSingleField(&ref, fieldName)
|
||||||
field, ok := typ.FieldByName(fieldName)
|
if err != nil {
|
||||||
if !ok {
|
return err
|
||||||
return fmt.Errorf("field %s not found", fieldName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tag := field.Tag.Get("storm")
|
field, ok := cfg.Fields[fieldName]
|
||||||
if tag == "" && fieldName != "ID" {
|
if !ok || (!field.IsID && field.Index == "") {
|
||||||
query := newQuery(n, q.StrictEq(fieldName, value))
|
query := newQuery(n, q.StrictEq(fieldName, value))
|
||||||
|
|
||||||
if n.tx != nil {
|
if n.tx != nil {
|
||||||
@@ -82,18 +79,12 @@ func (n *node) One(fieldName string, value interface{}, to interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var isID bool
|
|
||||||
if tag != "" {
|
|
||||||
tags := strings.Split(tag, ",")
|
|
||||||
isID = tags[0] == "id"
|
|
||||||
}
|
|
||||||
|
|
||||||
return n.readTx(func(tx *bolt.Tx) error {
|
return n.readTx(func(tx *bolt.Tx) error {
|
||||||
return n.one(tx, bucketName, fieldName, tag, to, val, fieldName == "ID" || isID)
|
return n.one(tx, bucketName, fieldName, cfg, to, val, field.IsID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) one(tx *bolt.Tx, bucketName, fieldName, tag string, to interface{}, val []byte, skipIndex bool) error {
|
func (n *node) one(tx *bolt.Tx, bucketName, fieldName string, cfg *structConfig, to interface{}, val []byte, skipIndex bool) error {
|
||||||
bucket := n.GetBucket(tx, bucketName)
|
bucket := n.GetBucket(tx, bucketName)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
@@ -101,7 +92,7 @@ func (n *node) one(tx *bolt.Tx, bucketName, fieldName, tag string, to interface{
|
|||||||
|
|
||||||
var id []byte
|
var id []byte
|
||||||
if !skipIndex {
|
if !skipIndex {
|
||||||
idx, err := getIndex(bucket, tag, fieldName)
|
idx, err := getIndex(bucket, cfg.Fields[fieldName].Index, fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == index.ErrNotFound {
|
if err == index.ErrNotFound {
|
||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
@@ -137,11 +128,10 @@ func (n *node) Find(fieldName string, value interface{}, to interface{}, options
|
|||||||
return ErrNoName
|
return ErrNoName
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := reflect.Indirect(sink.ref).Type().Elem()
|
ref := reflect.Indirect(reflect.New(sink.elemType))
|
||||||
|
cfg, err := extractSingleField(&ref, fieldName)
|
||||||
field, ok := typ.FieldByName(fieldName)
|
if err != nil {
|
||||||
if !ok {
|
return err
|
||||||
return fmt.Errorf("field %s not found", fieldName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := index.NewOptions()
|
opts := index.NewOptions()
|
||||||
@@ -149,8 +139,8 @@ func (n *node) Find(fieldName string, value interface{}, to interface{}, options
|
|||||||
fn(opts)
|
fn(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
tag := field.Tag.Get("storm")
|
field, ok := cfg.Fields[fieldName]
|
||||||
if tag == "" {
|
if !ok || (!field.IsID && field.Index == "") {
|
||||||
sink.limit = opts.Limit
|
sink.limit = opts.Limit
|
||||||
sink.skip = opts.Skip
|
sink.skip = opts.Skip
|
||||||
query := newQuery(n, q.StrictEq(fieldName, value))
|
query := newQuery(n, q.StrictEq(fieldName, value))
|
||||||
@@ -176,17 +166,17 @@ func (n *node) Find(fieldName string, value interface{}, to interface{}, options
|
|||||||
}
|
}
|
||||||
|
|
||||||
return n.readTx(func(tx *bolt.Tx) error {
|
return n.readTx(func(tx *bolt.Tx) error {
|
||||||
return n.find(tx, bucketName, fieldName, tag, sink, val, opts)
|
return n.find(tx, bucketName, fieldName, cfg, sink, val, opts)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) find(tx *bolt.Tx, bucketName, fieldName, tag string, sink *listSink, val []byte, opts *index.Options) error {
|
func (n *node) find(tx *bolt.Tx, bucketName, fieldName string, cfg *structConfig, sink *listSink, val []byte, opts *index.Options) error {
|
||||||
bucket := n.GetBucket(tx, bucketName)
|
bucket := n.GetBucket(tx, bucketName)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
idx, err := getIndex(bucket, tag, fieldName)
|
idx, err := getIndex(bucket, cfg.Fields[fieldName].Index, fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -341,11 +331,10 @@ func (n *node) Range(fieldName string, min, max, to interface{}, options ...func
|
|||||||
return ErrNoName
|
return ErrNoName
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := reflect.Indirect(sink.ref).Type().Elem()
|
ref := reflect.Indirect(reflect.New(sink.elemType))
|
||||||
|
cfg, err := extractSingleField(&ref, fieldName)
|
||||||
field, ok := typ.FieldByName(fieldName)
|
if err != nil {
|
||||||
if !ok {
|
return err
|
||||||
return fmt.Errorf("field %s not found", fieldName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := index.NewOptions()
|
opts := index.NewOptions()
|
||||||
@@ -353,8 +342,8 @@ func (n *node) Range(fieldName string, min, max, to interface{}, options ...func
|
|||||||
fn(opts)
|
fn(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
tag := field.Tag.Get("storm")
|
field, ok := cfg.Fields[fieldName]
|
||||||
if tag == "" {
|
if !ok || (!field.IsID && field.Index == "") {
|
||||||
sink.limit = opts.Limit
|
sink.limit = opts.Limit
|
||||||
sink.skip = opts.Skip
|
sink.skip = opts.Skip
|
||||||
query := newQuery(n, q.And(q.Gte(fieldName, min), q.Lte(fieldName, max)))
|
query := newQuery(n, q.And(q.Gte(fieldName, min), q.Lte(fieldName, max)))
|
||||||
@@ -384,23 +373,19 @@ func (n *node) Range(fieldName string, min, max, to interface{}, options ...func
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.tx != nil {
|
return n.readTx(func(tx *bolt.Tx) error {
|
||||||
return n.rnge(n.tx, bucketName, fieldName, tag, sink, mn, mx, opts)
|
return n.rnge(tx, bucketName, fieldName, cfg, sink, mn, mx, opts)
|
||||||
}
|
|
||||||
|
|
||||||
return n.s.Bolt.View(func(tx *bolt.Tx) error {
|
|
||||||
return n.rnge(tx, bucketName, fieldName, tag, sink, mn, mx, opts)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) rnge(tx *bolt.Tx, bucketName, fieldName, tag string, sink *listSink, min, max []byte, opts *index.Options) error {
|
func (n *node) rnge(tx *bolt.Tx, bucketName, fieldName string, cfg *structConfig, sink *listSink, min, max []byte, opts *index.Options) error {
|
||||||
bucket := n.GetBucket(tx, bucketName)
|
bucket := n.GetBucket(tx, bucketName)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
reflect.Indirect(sink.ref).SetLen(0)
|
reflect.Indirect(sink.ref).SetLen(0)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
idx, err := getIndex(bucket, tag, fieldName)
|
idx, err := getIndex(bucket, cfg.Fields[fieldName].Index, fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -51,9 +51,9 @@ func TestFind(t *testing.T) {
|
|||||||
|
|
||||||
users := []User{}
|
users := []User{}
|
||||||
|
|
||||||
err = db.Find("Age", "John", &users)
|
err = db.Find("unexportedField", "John", &users)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.EqualError(t, err, "field Age not found")
|
assert.EqualError(t, err, "field unexportedField not found")
|
||||||
|
|
||||||
err = db.Find("DateOfBirth", "John", &users)
|
err = db.Find("DateOfBirth", "John", &users)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
@@ -107,6 +107,10 @@ func TestFind(t *testing.T) {
|
|||||||
assert.Len(t, users, 10)
|
assert.Len(t, users, 10)
|
||||||
assert.Equal(t, 21, users[0].ID)
|
assert.Equal(t, 21, users[0].ID)
|
||||||
assert.Equal(t, 30, users[9].ID)
|
assert.Equal(t, 30, users[9].ID)
|
||||||
|
|
||||||
|
// err = db.Find("Age", 10, &users)
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindIntIndex(t *testing.T) {
|
func TestFindIntIndex(t *testing.T) {
|
||||||
@@ -153,7 +157,7 @@ func TestAllByIndex(t *testing.T) {
|
|||||||
|
|
||||||
err = db.AllByIndex("Unknown field", &users)
|
err = db.AllByIndex("Unknown field", &users)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, ErrNotFound == err)
|
assert.Equal(t, ErrNotFound, err)
|
||||||
|
|
||||||
err = db.AllByIndex("DateOfBirth", &users)
|
err = db.AllByIndex("DateOfBirth", &users)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -526,7 +530,7 @@ func TestRange(t *testing.T) {
|
|||||||
|
|
||||||
err = db.Range("Age", min, max, &users)
|
err = db.Range("Age", min, max, &users)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.EqualError(t, err, "field Age not found")
|
assert.EqualError(t, err, "not found")
|
||||||
|
|
||||||
dateMin := time.Now().Add(-time.Duration(50) * time.Hour)
|
dateMin := time.Now().Add(-time.Duration(50) * time.Hour)
|
||||||
dateMax := dateMin.Add(time.Duration(3) * time.Hour)
|
dateMax := dateMin.Add(time.Duration(3) * time.Hour)
|
||||||
|
@@ -64,5 +64,6 @@ func (m *meta) increment(field *fieldConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
field.Value.Set(reflect.ValueOf(counter).Convert(field.Value.Type()))
|
field.Value.Set(reflect.ValueOf(counter).Convert(field.Value.Type()))
|
||||||
|
field.IsZero = false
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -321,6 +321,12 @@ func TestSaveIncrement(t *testing.T) {
|
|||||||
err = db.One("Identifier", i, &s2)
|
err = db.One("Identifier", i, &s2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, s1, s2)
|
require.Equal(t, s1, s2)
|
||||||
|
|
||||||
|
var list []User
|
||||||
|
err = db.Find("Age", i, &list)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, list, 1)
|
||||||
|
require.Equal(t, s1, list[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -62,12 +62,13 @@ type ClassicInline struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int `storm:"id"`
|
ID int `storm:"id"`
|
||||||
Name string `storm:"index"`
|
Name string `storm:"index"`
|
||||||
age int
|
Age int `storm:"index,increment"`
|
||||||
DateOfBirth time.Time `storm:"index"`
|
DateOfBirth time.Time `storm:"index"`
|
||||||
Group string
|
Group string
|
||||||
Slug string `storm:"unique"`
|
unexportedField int
|
||||||
|
Slug string `storm:"unique"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ToEmbed struct {
|
type ToEmbed struct {
|
||||||
|
Reference in New Issue
Block a user