// Range returns one or more records by the specified index within the specified range func (n *node) Range(fieldName string, min, max, to interface{}, options ...func(*index.Options)) error { sink, err := newListSink(n, to) if err != nil { return err } bucketName := sink.bucketName() if bucketName == "" { return ErrNoName } ref := reflect.Indirect(reflect.New(sink.elemType)) cfg, err := extractSingleField(&ref, fieldName) if err != nil { return err } opts := index.NewOptions() for _, fn := range options { fn(opts) } field, ok := cfg.Fields[fieldName] if !ok || (!field.IsID && field.Index == "") { sink.limit = opts.Limit sink.skip = opts.Skip query := newQuery(n, q.And(q.Gte(fieldName, min), q.Lte(fieldName, max))) if opts.Reverse { query.Reverse() } err = n.readTx(func(tx *bolt.Tx) error { return query.query(tx, sink) }) if err != nil { return err } return sink.flush() } mn, err := toBytes(min, n.s.codec) if err != nil { return err } mx, err := toBytes(max, n.s.codec) if err != nil { return err } return n.readTx(func(tx *bolt.Tx) error { return n.rnge(tx, bucketName, fieldName, cfg, sink, mn, mx, opts) }) }
// Find returns one or more records by the specified index func (n *node) Find(fieldName string, value interface{}, to interface{}, options ...func(q *index.Options)) error { if value == nil { return ErrNotFound } sink, err := newListSink(n, to) if err != nil { return err } bucketName := sink.bucketName() if bucketName == "" { return ErrNoName } ref := reflect.Indirect(reflect.New(sink.elemType)) cfg, err := extractSingleField(&ref, fieldName) if err != nil { return err } opts := index.NewOptions() for _, fn := range options { fn(opts) } field, ok := cfg.Fields[fieldName] if !ok || (!field.IsID && field.Index == "") { sink.limit = opts.Limit sink.skip = opts.Skip query := newQuery(n, q.StrictEq(fieldName, value)) if opts.Reverse { query.Reverse() } err = n.readTx(func(tx *bolt.Tx) error { return query.query(tx, sink) }) if err != nil { return err } return sink.flush() } val, err := toBytes(value, n.s.codec) if err != nil { return err } return n.readTx(func(tx *bolt.Tx) error { return n.find(tx, bucketName, fieldName, cfg, sink, val, opts) }) }
// All gets all the records of a bucket. // If there are no records it returns no error and the 'to' parameter is set to an empty slice. func (n *node) All(to interface{}, options ...func(*index.Options)) error { opts := index.NewOptions() for _, fn := range options { fn(opts) } query := newQuery(n, nil).Limit(opts.Limit).Skip(opts.Skip) if opts.Reverse { query.Reverse() } err := query.Find(to) if err != nil && err != ErrNotFound { return err } if err == ErrNotFound { ref := reflect.ValueOf(to) results := reflect.MakeSlice(reflect.Indirect(ref).Type(), 0, 0) reflect.Indirect(ref).Set(results) } return nil }
// AllByIndex gets all the records of a bucket that are indexed in the specified index func (n *node) AllByIndex(fieldName string, to interface{}, options ...func(*index.Options)) error { if fieldName == "" { return n.All(to, options...) } ref := reflect.ValueOf(to) if ref.Kind() != reflect.Ptr || ref.Elem().Kind() != reflect.Slice { return ErrSlicePtrNeeded } typ := reflect.Indirect(ref).Type().Elem() if typ.Kind() == reflect.Ptr { typ = typ.Elem() } newElem := reflect.New(typ) cfg, err := extract(&newElem) if err != nil { return err } if cfg.ID.Name == fieldName { return n.All(to, options...) } opts := index.NewOptions() for _, fn := range options { fn(opts) } return n.readTx(func(tx *bolt.Tx) error { return n.allByIndex(tx, fieldName, cfg, &ref, opts) }) }
func TestListIndexRange(t *testing.T) { dir, _ := ioutil.TempDir(os.TempDir(), "storm") defer os.RemoveAll(dir) db, _ := storm.Open(filepath.Join(dir, "storm.db")) defer db.Close() db.Bolt.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("test")) assert.NoError(t, err) idx, err := index.NewListIndex(b, []byte("index1")) assert.NoError(t, err) for i := 0; i < 10; i++ { val, _ := gob.Codec.Marshal(i) err = idx.Add(val, val) assert.NoError(t, err) } min, _ := gob.Codec.Marshal(3) max, _ := gob.Codec.Marshal(5) list, err := idx.Range(min, max, nil) assert.Len(t, list, 3) assert.NoError(t, err) assertEncodedIntListEqual(t, []int{3, 4, 5}, list) min, _ = gob.Codec.Marshal(11) max, _ = gob.Codec.Marshal(20) list, err = idx.Range(min, max, nil) assert.Len(t, list, 0) assert.NoError(t, err) min, _ = gob.Codec.Marshal(7) max, _ = gob.Codec.Marshal(2) list, err = idx.Range(min, max, nil) assert.Len(t, list, 0) assert.NoError(t, err) min, _ = gob.Codec.Marshal(-5) max, _ = gob.Codec.Marshal(2) list, err = idx.Range(min, max, nil) assert.Len(t, list, 0) assert.NoError(t, err) min, _ = gob.Codec.Marshal(3) max, _ = gob.Codec.Marshal(7) opts := index.NewOptions() opts.Skip = 2 list, err = idx.Range(min, max, opts) assert.Len(t, list, 3) assert.NoError(t, err) assertEncodedIntListEqual(t, []int{5, 6, 7}, list) opts = index.NewOptions() opts.Limit = 2 list, err = idx.Range(min, max, opts) assert.Len(t, list, 2) assert.NoError(t, err) assertEncodedIntListEqual(t, []int{3, 4}, list) opts = index.NewOptions() opts.Reverse = true opts.Skip = 2 opts.Limit = 2 list, err = idx.Range(min, max, opts) assert.Len(t, list, 2) assert.NoError(t, err) assertEncodedIntListEqual(t, []int{5, 4}, list) return nil }) }
func TestListIndexAllRecords(t *testing.T) { dir, _ := ioutil.TempDir(os.TempDir(), "storm") defer os.RemoveAll(dir) db, _ := storm.Open(filepath.Join(dir, "storm.db")) defer db.Close() db.Bolt.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("test")) assert.NoError(t, err) idx, err := index.NewListIndex(b, []byte("lindex1")) assert.NoError(t, err) ids, err := idx.AllRecords(nil) assert.NoError(t, err) assert.Len(t, ids, 0) err = idx.Add([]byte("goodbye"), []byte("id2")) assert.NoError(t, err) assert.Equal(t, 1, countItems(t, idx.IndexBucket)) err = idx.Add([]byte("goodbye"), []byte("id1")) assert.NoError(t, err) assert.Equal(t, 2, countItems(t, idx.IndexBucket)) err = idx.Add([]byte("hello"), []byte("id4")) assert.NoError(t, err) assert.Equal(t, 3, countItems(t, idx.IndexBucket)) err = idx.Add([]byte("hello"), []byte("id3")) assert.NoError(t, err) assert.Equal(t, 4, countItems(t, idx.IndexBucket)) ids, err = idx.AllRecords(nil) assert.NoError(t, err) assert.Len(t, ids, 4) assert.Equal(t, []byte("id1"), ids[0]) assert.Equal(t, []byte("id2"), ids[1]) assert.Equal(t, []byte("id3"), ids[2]) assert.Equal(t, []byte("id4"), ids[3]) err = idx.RemoveID([]byte("id1")) assert.NoError(t, err) assert.Equal(t, 3, countItems(t, idx.IndexBucket)) ids, err = idx.AllRecords(nil) assert.NoError(t, err) assert.Len(t, ids, 3) assert.Equal(t, []byte("id2"), ids[0]) err = idx.Add([]byte("goodbye"), []byte("id1")) assert.NoError(t, err) assert.Equal(t, 4, countItems(t, idx.IndexBucket)) opts := index.NewOptions() opts.Limit = 1 ids, err = idx.AllRecords(opts) assert.Len(t, ids, 1) opts = index.NewOptions() opts.Skip = 2 ids, err = idx.AllRecords(opts) assert.Len(t, ids, 2) opts = index.NewOptions() opts.Skip = 2 opts.Limit = 3 opts.Reverse = true ids, err = idx.AllRecords(opts) assert.NoError(t, err) assert.Len(t, ids, 2) assert.Equal(t, []byte("id2"), ids[0]) return nil }) }
func TestListIndex(t *testing.T) { dir, _ := ioutil.TempDir(os.TempDir(), "storm") defer os.RemoveAll(dir) db, _ := storm.Open(filepath.Join(dir, "storm.db")) defer db.Close() err := db.Bolt.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("test")) assert.NoError(t, err) idx, err := index.NewListIndex(b, []byte("lindex1")) assert.NoError(t, err) err = idx.Add([]byte("hello"), []byte("id1")) assert.NoError(t, err) err = idx.Add([]byte("hello"), []byte("id1")) assert.NoError(t, err) err = idx.Add([]byte("hello"), []byte("id2")) assert.NoError(t, err) err = idx.Add([]byte("goodbye"), []byte("id2")) assert.NoError(t, err) err = idx.Add(nil, []byte("id2")) assert.Error(t, err) assert.Equal(t, index.ErrNilParam, err) err = idx.Add([]byte("hi"), nil) assert.Error(t, err) assert.Equal(t, index.ErrNilParam, err) ids, err := idx.All([]byte("hello"), nil) assert.NoError(t, err) assert.Len(t, ids, 1) assert.Equal(t, []byte("id1"), ids[0]) ids, err = idx.All([]byte("goodbye"), nil) assert.Len(t, ids, 1) assert.Equal(t, []byte("id2"), ids[0]) ids, err = idx.All([]byte("yo"), nil) assert.Nil(t, ids) err = idx.RemoveID([]byte("id2")) assert.NoError(t, err) ids, err = idx.All([]byte("goodbye"), nil) assert.Len(t, ids, 0) err = idx.RemoveID(nil) assert.NoError(t, err) err = idx.RemoveID([]byte("id1")) assert.NoError(t, err) err = idx.RemoveID([]byte("id2")) assert.NoError(t, err) err = idx.RemoveID([]byte("id3")) assert.NoError(t, err) ids, err = idx.All([]byte("hello"), nil) assert.NoError(t, err) assert.Nil(t, ids) err = idx.Add([]byte("hello"), []byte("id1")) assert.NoError(t, err) err = idx.Add([]byte("hi"), []byte("id2")) assert.NoError(t, err) err = idx.Add([]byte("yo"), []byte("id3")) assert.NoError(t, err) err = idx.RemoveID([]byte("id2")) assert.NoError(t, err) ids, err = idx.All([]byte("hello"), nil) assert.Len(t, ids, 1) assert.Equal(t, []byte("id1"), ids[0]) ids, err = idx.All([]byte("hi"), nil) assert.Len(t, ids, 0) ids, err = idx.All([]byte("yo"), nil) assert.Len(t, ids, 1) assert.Equal(t, []byte("id3"), ids[0]) err = idx.RemoveID([]byte("id2")) assert.NoError(t, err) err = idx.RemoveID([]byte("id4")) assert.NoError(t, err) err = idx.Add([]byte("hey"), []byte("id1")) err = idx.Add([]byte("hey"), []byte("id2")) err = idx.Add([]byte("hey"), []byte("id3")) err = idx.Add([]byte("hey"), []byte("id4")) ids, err = idx.All([]byte("hey"), nil) assert.Len(t, ids, 4) opts := index.NewOptions() opts.Limit = 1 ids, err = idx.All([]byte("hey"), opts) assert.Len(t, ids, 1) opts = index.NewOptions() opts.Skip = 2 ids, err = idx.All([]byte("hey"), opts) assert.Len(t, ids, 2) opts = index.NewOptions() opts.Skip = 2 opts.Limit = 3 opts.Reverse = true ids, err = idx.All([]byte("hey"), opts) assert.Len(t, ids, 2) assert.Equal(t, []byte("id2"), ids[0]) id := idx.Get([]byte("hey")) assert.Equal(t, []byte("id1"), id) err = idx.Remove([]byte("hey")) assert.NoError(t, err) ids, err = idx.All([]byte("hey"), nil) assert.NoError(t, err) assert.Len(t, ids, 0) ids, err = idx.All([]byte("hey"), nil) assert.NoError(t, err) assert.Len(t, ids, 0) return nil }) assert.NoError(t, err) }
func TestUniqueIndex(t *testing.T) { dir, _ := ioutil.TempDir(os.TempDir(), "storm") defer os.RemoveAll(dir) db, _ := storm.Open(filepath.Join(dir, "storm.db")) defer db.Close() err := db.Bolt.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("test")) assert.NoError(t, err) idx, err := index.NewUniqueIndex(b, []byte("uindex1")) assert.NoError(t, err) err = idx.Add([]byte("hello"), []byte("id1")) assert.NoError(t, err) err = idx.Add([]byte("hello"), []byte("id1")) assert.NoError(t, err) err = idx.Add([]byte("hello"), []byte("id2")) assert.Error(t, err) assert.Equal(t, index.ErrAlreadyExists, err) err = idx.Add(nil, []byte("id2")) assert.Error(t, err) assert.Equal(t, index.ErrNilParam, err) err = idx.Add([]byte("hi"), nil) assert.Error(t, err) assert.Equal(t, index.ErrNilParam, err) id := idx.Get([]byte("hello")) assert.Equal(t, []byte("id1"), id) id = idx.Get([]byte("goodbye")) assert.Nil(t, id) err = idx.Remove([]byte("hello")) assert.NoError(t, err) err = idx.Remove(nil) assert.NoError(t, err) id = idx.Get([]byte("hello")) assert.Nil(t, id) err = idx.Add([]byte("hello"), []byte("id1")) assert.NoError(t, err) err = idx.Add([]byte("hi"), []byte("id2")) assert.NoError(t, err) err = idx.Add([]byte("yo"), []byte("id3")) assert.NoError(t, err) list, err := idx.AllRecords(nil) assert.NoError(t, err) assert.Len(t, list, 3) opts := index.NewOptions() opts.Limit = 2 list, err = idx.AllRecords(opts) assert.NoError(t, err) assert.Len(t, list, 2) opts = index.NewOptions() opts.Skip = 2 list, err = idx.AllRecords(opts) assert.NoError(t, err) assert.Len(t, list, 1) assert.Equal(t, []byte("id3"), list[0]) opts = index.NewOptions() opts.Skip = 2 opts.Limit = 1 opts.Reverse = true list, err = idx.AllRecords(opts) assert.NoError(t, err) assert.Len(t, list, 1) assert.Equal(t, []byte("id1"), list[0]) err = idx.RemoveID([]byte("id2")) assert.NoError(t, err) id = idx.Get([]byte("hello")) assert.Equal(t, []byte("id1"), id) id = idx.Get([]byte("hi")) assert.Nil(t, id) id = idx.Get([]byte("yo")) assert.Equal(t, []byte("id3"), id) ids, err := idx.All([]byte("yo"), nil) assert.NoError(t, err) assert.Len(t, ids, 1) assert.Equal(t, []byte("id3"), ids[0]) err = idx.RemoveID([]byte("id2")) assert.NoError(t, err) err = idx.RemoveID([]byte("id4")) assert.NoError(t, err) return nil }) assert.NoError(t, err) }