-
Notifications
You must be signed in to change notification settings - Fork 1
/
datastore.go
132 lines (118 loc) · 3.43 KB
/
datastore.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package dsbbolt
import (
"os"
"bytes"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/query"
"go.etcd.io/bbolt"
// "github.com/ipfs/go-datastore"
)
var (
defaultBucket = []byte("datastore")
_ datastore.Batching = (*Datastore)(nil)
)
// Datastore implements an ipfs datastore
// backed by a bbolt db
type Datastore struct {
db *bbolt.DB
bucket []byte
}
// NewDatastore is used to instantiate our datastore
func NewDatastore(path string, opts *bbolt.Options, bucket []byte) (*Datastore, error) {
db, err := bbolt.Open(path, os.FileMode(0640), nil)
if err != nil {
return nil, err
}
if bucket == nil {
bucket = defaultBucket
}
if err := db.Update(func(tx *bbolt.Tx) error {
_, err := tx.CreateBucketIfNotExists(bucket)
return err
}); err != nil {
return nil, err
}
return &Datastore{db, bucket}, nil
}
// Put is used to store something in our underlying datastore
func (d *Datastore) Put(key datastore.Key, value []byte) error {
return d.db.Update(func(tx *bbolt.Tx) error {
return tx.Bucket(d.bucket).Put(key.Bytes(), value)
})
}
// Delete removes a key/value pair from our datastore
func (d *Datastore) Delete(key datastore.Key) error {
return d.db.Update(func(tx *bbolt.Tx) error {
return tx.Bucket(d.bucket).Delete(key.Bytes())
})
}
// Get is used to retrieve a value from the datastore
func (d *Datastore) Get(key datastore.Key) ([]byte, error) {
var data []byte
if err := d.db.View(func(tx *bbolt.Tx) error {
data = tx.Bucket(d.bucket).Get(key.Bytes())
return nil
}); err != nil {
return nil, err
}
return data, nil
}
// Has returns whether the key is present in our datastore
func (d *Datastore) Has(key datastore.Key) (bool, error) {
data, err := d.Get(key)
if err != nil {
return false, err
}
return data != nil, nil
}
// GetSize returns the size of the value referenced by key
func (d *Datastore) GetSize(key datastore.Key) (int, error) {
return datastore.GetBackedSize(d, key)
}
// Query performs a complex search query on the underlying datastore
// For more information see :
// https://github.com/ipfs/go-datastore/blob/aa9190c18f1576be98e974359fd08c64ca0b5a94/examples/fs.go#L96
// https://github.com/etcd-io/bbolt#prefix-scans
func (d *Datastore) Query(q query.Query) (query.Results, error) {
var entries []query.Entry
if err := d.db.View(func(tx *bbolt.Tx) error {
cursor := tx.Bucket(d.bucket).Cursor()
if q.Prefix == "" {
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
var entry query.Entry
entry.Key = string(k)
if !q.KeysOnly {
entry.Value = v
entry.Size = int(len(entry.Value))
}
entries = append(entries, entry)
}
return nil
}
pref := []byte(q.Prefix)
for k, v := cursor.Seek(pref); k != nil && bytes.HasPrefix(k, pref); k, v = cursor.Next() {
var entry query.Entry
entry.Key = string(k)
if !q.KeysOnly {
entry.Value = v
}
entries = append(entries, entry)
}
return nil
}); err != nil {
return nil, err
}
results := query.ResultsWithEntries(q, entries)
// close the result builder since we are done using it
return results, nil
}
// Batch returns a basic batched bolt datastore wrapper
// it is a temporary method until we implement a proper
// transactional batched datastore
func (d *Datastore) Batch() (datastore.Batch, error) {
return datastore.NewBasicBatch(d), nil
}
// Close is used to close the underlying datastore
func (d *Datastore) Close() error {
return d.db.Close()
}