-
Notifications
You must be signed in to change notification settings - Fork 4
/
storage_boltdb.go
177 lines (140 loc) · 3.7 KB
/
storage_boltdb.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package storage
import (
"errors"
"io/fs"
"os"
"path/filepath"
"github.com/asdine/storm/v3"
"github.com/asdine/storm/v3/q"
"github.com/vgarvardt/rklotz/pkg/model"
)
const tagsNode = "__rklotz_tags"
// BoltDBStorage is the Storage implementation on top of BoltDB
type BoltDBStorage struct {
db *storm.DB
path string
tags storm.Node
postsCount int
postsPerPage int
}
// NewBoltDBStorage creates new BoltDBStorage instance
func NewBoltDBStorage(path string, postsPerPage int) (*BoltDBStorage, error) {
var err error
instance := &BoltDBStorage{path: path, postsPerPage: postsPerPage}
err = os.MkdirAll(filepath.Dir(path), 0755)
if nil != err {
return nil, err
}
err = instance.remove()
if nil != err {
return nil, err
}
instance.db, err = storm.Open(path)
if nil != err {
return nil, err
}
instance.tags = instance.db.From(tagsNode)
// Initialize buckets and indexes before saving an object
// Useful when starting your application
err = instance.db.Init(&model.Post{})
if nil != err {
return nil, err
}
err = instance.tags.Init(&model.Tag{})
if nil != err {
return nil, err
}
return instance, nil
}
// Save persists new post in the storage
func (s *BoltDBStorage) Save(post *model.Post) error {
err := s.db.Save(post)
if nil != err {
return err
}
for i := range post.Tags {
var tag model.Tag
err := s.tags.One("Tag", post.Tags[i], &tag)
if errors.Is(err, storm.ErrNotFound) {
tag = model.Tag{Tag: post.Tags[i], Paths: []string{}}
} else if nil != err {
return err
}
tag.Paths = append(tag.Paths, post.Path)
err = s.tags.Save(&tag)
if nil != err {
return err
}
}
s.postsCount++
return nil
}
// Finalize is called after all posts are persisted in the storage
func (s *BoltDBStorage) Finalize() error {
return nil
}
// FindByPath searches for a post by path
func (s *BoltDBStorage) FindByPath(path string) (*model.Post, error) {
var post model.Post
err := s.db.One("Path", path, &post)
if errors.Is(err, storm.ErrNotFound) {
return nil, ErrorNotFound
}
return &post, err
}
// ListAll returns ordered by date posts page
func (s *BoltDBStorage) ListAll(page int) ([]*model.Post, error) {
var posts []*model.Post
offset := page * s.postsPerPage
err := s.db.AllByIndex("PublishedAt", &posts, storm.Limit(s.postsPerPage), storm.Skip(offset), storm.Reverse())
return posts, err
}
// ListTag returns ordered by date posts page for a tag
func (s *BoltDBStorage) ListTag(tag string, page int) ([]*model.Post, error) {
var tagObject model.Tag
err := s.tags.One("Tag", tag, &tagObject)
if errors.Is(err, storm.ErrNotFound) {
return nil, ErrorNotFound
}
var posts []*model.Post
offset := page * s.postsPerPage
query := s.db.Select(q.In("Path", tagObject.Paths)).Limit(s.postsPerPage).Skip(offset).OrderBy("PublishedAt").Reverse()
err = query.Find(&posts)
if err == storm.ErrNotFound {
return []*model.Post{}, nil
}
if nil != err {
return nil, err
}
return posts, nil
}
// Close closes the storage and frees all resources
func (s *BoltDBStorage) Close() error {
if err := s.db.Close(); err != nil {
return err
}
return s.remove()
}
// Meta returns metadata for all persisted posts
func (s *BoltDBStorage) Meta() *model.Meta {
return model.NewMeta(s.postsCount, s.postsPerPage)
}
// TagMeta returns metadata for all persisted posts for a tag
func (s *BoltDBStorage) TagMeta(tag string) *model.Meta {
var tagObject model.Tag
err := s.tags.One("Tag", tag, &tagObject)
if err != nil {
return &model.Meta{}
}
return model.NewMeta(len(tagObject.Paths), s.postsPerPage)
}
func (s *BoltDBStorage) remove() error {
_, err := os.Stat(s.path)
if err == nil {
return os.Remove(s.path)
}
if errors.Is(err, fs.ErrNotExist) {
return nil
}
return err
}