Compare commits
11 commits
Author | SHA1 | Date | |
---|---|---|---|
|
ecfd279d7e | ||
|
c96da91077 | ||
|
ddc31e4ca5 | ||
|
d14b45abdf | ||
|
c03c377165 | ||
|
2234d83153 | ||
|
cb02d4a1c5 | ||
f76492a71e | |||
|
28848e639c | ||
|
3885d53a40 | ||
|
853b8d8929 |
7 changed files with 301 additions and 23 deletions
10
.drone.yml
Normal file
10
.drone.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
steps:
|
||||
- name: backend
|
||||
image: golang
|
||||
commands:
|
||||
- go build
|
||||
- go test
|
33
CHANGELOG
33
CHANGELOG
|
@ -1,3 +1,34 @@
|
|||
v1.0.7, released 2025-03-16
|
||||
* bugfixes
|
||||
- GetRandomNode public command
|
||||
|
||||
v1.0.6, released 2025-03-16
|
||||
* features
|
||||
- Node.Get - get object property value
|
||||
|
||||
v1.0.5, released 2025-03-16
|
||||
* bugfixes
|
||||
- FixType - run recursevily into child nodes to get correct value
|
||||
* features
|
||||
- Length - get length of list
|
||||
- GetRandomNode - get random node from list
|
||||
|
||||
v1.0.4, released 2025-01-07
|
||||
* abcex to v4
|
||||
|
||||
v1.0.3, released 2024-09-20
|
||||
* abcex to v3
|
||||
|
||||
v1.0.2, released 2024-08-29
|
||||
* use GetNode interface
|
||||
|
||||
v1.0.1, released 2024-08-08
|
||||
* split commands to value and object layer
|
||||
* abcex to v1.0.1
|
||||
* features
|
||||
- CreateNode - create directory to hold key/value as object or as list for objects
|
||||
- AddObject - create directory in list object by abcex id
|
||||
|
||||
v1.0.0, released 2024-07-18
|
||||
* features
|
||||
- Init
|
||||
|
@ -7,5 +38,3 @@ v1.0.0, released 2024-07-18
|
|||
- Inc - increase by abcex value
|
||||
- Dec - decrease by abcex value
|
||||
- Now - insert datetime NOW
|
||||
|
||||
|
||||
|
|
46
README.md
46
README.md
|
@ -1,24 +1,50 @@
|
|||
# zordfsdb
|
||||
|
||||
Simple filesystem based key/value db. Provided as golang lib.
|
||||
[](https://drone.arns.lt/zordsdavini/zordfsdb)
|
||||
|
||||
Simple filesystem based key/value db. Provided as golang lib. Main idea is that user knows structure and gets value from known path. For operations that should search or do more should be used Nodes (golang structure).
|
||||
|
||||
## Configuration
|
||||
|
||||
Init root directory
|
||||
Init root directory.
|
||||
|
||||
## Supported command
|
||||
|
||||
* GET - to get value. Depends on return type. Can be single value or map (to object or to list of object)
|
||||
* INS - insert object into list
|
||||
* SAVE - update value or object. Depends on path
|
||||
* INC - increase abcex value
|
||||
* DEC - decrease abcex value
|
||||
* NOW - save current datetime
|
||||
* DEL - delete
|
||||
* KEYS - return possible keys
|
||||
Commands can be split into value layer when you know the structure and node layer for deaper operations.
|
||||
|
||||
### value layer commands
|
||||
|
||||
* Get - to get value from path
|
||||
* Save - update or create value. Depends on path. If path directs into not existing parent object - will return false
|
||||
* Inc - increase abcex value
|
||||
* Dec - decrease abcex value
|
||||
* Now - save current datetime
|
||||
|
||||
### node layer commands
|
||||
|
||||
* GetNode - to get node. It can be object, list or value object. Returns false if not exist
|
||||
* CreatNode - create list (director) to add many same objects (structure should be controlled by user) or single object. Parent Node should exist
|
||||
* AddObject - add object to given list. Should assign abcex id
|
||||
* Length - get length of list
|
||||
* GetRandomNode - get random object from list
|
||||
|
||||
### Node as Object commands
|
||||
|
||||
* Node.Get - get object property value
|
||||
* Node.GetNodes - get object properties
|
||||
* Node.FixType - checks and sets object/list type
|
||||
|
||||
### helper commands
|
||||
|
||||
* Del - delete value, object or list by given path
|
||||
* Keys - return possible keys for object or ids for list
|
||||
|
||||
## dictionary
|
||||
|
||||
* object - directory of key/value
|
||||
* list - directory of objects named by abcex as key
|
||||
* path - path to key or object or list
|
||||
|
||||
## DEV
|
||||
|
||||
Run tests: `$ go test`
|
||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module g.arns.lt/zordsdavini/zordfsdb
|
|||
go 1.22.5
|
||||
|
||||
require (
|
||||
g.arns.lt/zordsdavini/abcex v1.0.0
|
||||
g.arns.lt/zordsdavini/abcex/v4 v4.0.4
|
||||
github.com/otiai10/copy v1.14.0
|
||||
)
|
||||
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1,5 +1,5 @@
|
|||
g.arns.lt/zordsdavini/abcex v1.0.0 h1:qQqlZ4DMfethCGK4I6yGaLqMrTzKNIshqpINd1l3t0E=
|
||||
g.arns.lt/zordsdavini/abcex v1.0.0/go.mod h1:YRcJgts3XZwI+LEkngpfUab3DkUAW387Irpr43hIym8=
|
||||
g.arns.lt/zordsdavini/abcex/v4 v4.0.4 h1:idjvgkCjrjZfDxLyOcX7lCIdIndISDAkj77VCvhu8/c=
|
||||
g.arns.lt/zordsdavini/abcex/v4 v4.0.4/go.mod h1:/+//gYSUtJrdsmTtWNoffRO4xD1BuPRUMGW4ynet7iE=
|
||||
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
||||
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
||||
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
|
||||
|
|
127
zordfsdb.go
127
zordfsdb.go
|
@ -1,14 +1,20 @@
|
|||
package zordfsdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"g.arns.lt/zordsdavini/abcex"
|
||||
abcex "g.arns.lt/zordsdavini/abcex/v4"
|
||||
)
|
||||
|
||||
type GetNodes interface {
|
||||
GetNodes() map[string]Node
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
Root string
|
||||
Nodes map[string]Node
|
||||
|
@ -22,6 +28,22 @@ type Node struct {
|
|||
List bool
|
||||
}
|
||||
|
||||
func (db *DB) GetNodes() map[string]Node {
|
||||
return db.Nodes
|
||||
}
|
||||
|
||||
func (node *Node) GetNodes() map[string]Node {
|
||||
return node.Nodes
|
||||
}
|
||||
|
||||
func (node *Node) Get(key string) (string, error) {
|
||||
if _, ok := node.Nodes[key]; ok {
|
||||
return node.Nodes[key].Value, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Key not found")
|
||||
}
|
||||
|
||||
func InitDB(root string) (DB, error) {
|
||||
db := DB{Root: root}
|
||||
err := db.Refresh()
|
||||
|
@ -98,6 +120,8 @@ func (node *Node) FixType() {
|
|||
node.List = false
|
||||
node.Object = false
|
||||
for _, child := range node.Nodes {
|
||||
child.FixType()
|
||||
|
||||
if child.Object {
|
||||
node.List = true
|
||||
break
|
||||
|
@ -159,12 +183,12 @@ func (db *DB) Inc(vpath string) bool {
|
|||
|
||||
val, found := db.Get(vpath)
|
||||
if !found {
|
||||
return false
|
||||
val = "0"
|
||||
}
|
||||
|
||||
valAbc := abcex.Decode(val)
|
||||
valAbc := abcex.Decode(val, abcex.BASE62)
|
||||
valAbc++
|
||||
val = abcex.Encode(valAbc)
|
||||
val = abcex.Encode(valAbc, abcex.BASE62)
|
||||
|
||||
os.WriteFile(path.Join(fullPath...), []byte(string(val)), 0644)
|
||||
err := db.Refresh()
|
||||
|
@ -181,12 +205,12 @@ func (db *DB) Dec(vpath string) bool {
|
|||
|
||||
val, found := db.Get(vpath)
|
||||
if !found {
|
||||
return false
|
||||
val = "0"
|
||||
}
|
||||
|
||||
valAbc := abcex.Decode(val)
|
||||
valAbc := abcex.Decode(val, abcex.BASE62)
|
||||
valAbc--
|
||||
val = abcex.Encode(valAbc)
|
||||
val = abcex.Encode(valAbc, abcex.BASE62)
|
||||
|
||||
os.WriteFile(path.Join(fullPath...), []byte(string(val)), 0644)
|
||||
err := db.Refresh()
|
||||
|
@ -244,3 +268,92 @@ func (db *DB) Del(vpath string) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func (db *DB) CreateNode(vpath string) bool {
|
||||
fullPath := []string{db.Root}
|
||||
fullPath = append(fullPath, strings.Split(vpath, ".")...)
|
||||
|
||||
err := os.Mkdir(path.Join(fullPath...), 0750)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
err = db.Refresh()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (db *DB) AddObject(vpath string) (string, error) {
|
||||
fullPath := []string{db.Root}
|
||||
fullPath = append(fullPath, strings.Split(vpath, ".")...)
|
||||
root := path.Join(fullPath...)
|
||||
ids, err := os.ReadDir(root)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
maxId := int64(0)
|
||||
notEmpty := false
|
||||
for _, id := range ids {
|
||||
idInt := abcex.Decode(id.Name(), abcex.BASE62)
|
||||
if maxId <= idInt {
|
||||
maxId = idInt
|
||||
notEmpty = true
|
||||
}
|
||||
}
|
||||
|
||||
if notEmpty {
|
||||
maxId++
|
||||
}
|
||||
|
||||
fullPath = append(fullPath, abcex.Encode(maxId, abcex.BASE62))
|
||||
fmt.Println(fullPath, maxId, abcex.Encode(maxId, abcex.BASE62))
|
||||
|
||||
err = os.Mkdir(path.Join(fullPath...), 0750)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = db.Refresh()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return abcex.Encode(maxId, abcex.BASE62), nil
|
||||
}
|
||||
|
||||
func (db *DB) Length(vpath string) (int64, error) {
|
||||
node, found := db.GetNode(vpath)
|
||||
if !found {
|
||||
return 0, fmt.Errorf("Node not found")
|
||||
}
|
||||
|
||||
if !node.List {
|
||||
return 0, fmt.Errorf("Node is not a list")
|
||||
}
|
||||
|
||||
return int64(len(node.Nodes)), nil
|
||||
}
|
||||
|
||||
func (db *DB) GetRandomNode(vpath string) (Node, error) {
|
||||
node, found := db.GetNode(vpath)
|
||||
if !found {
|
||||
return Node{}, fmt.Errorf("Node not found")
|
||||
}
|
||||
|
||||
if !node.List {
|
||||
return Node{}, fmt.Errorf("Not a list")
|
||||
}
|
||||
|
||||
if len(node.Nodes) == 0 {
|
||||
return Node{}, fmt.Errorf("List is empty")
|
||||
}
|
||||
|
||||
keys := db.Keys(vpath)
|
||||
randomKey := keys[rand.Intn(len(keys))]
|
||||
|
||||
return node.Nodes[randomKey], nil
|
||||
}
|
||||
|
|
102
zordfsdb_test.go
102
zordfsdb_test.go
|
@ -119,7 +119,7 @@ func TestNow(t *testing.T) {
|
|||
db.Now("today")
|
||||
val, found := db.Get("today")
|
||||
fmt.Println(val)
|
||||
if !found || len(val) != 25 {
|
||||
if !found || len(val) < 10 {
|
||||
t.Fatal("today value length wrong:", len(val))
|
||||
}
|
||||
}
|
||||
|
@ -172,3 +172,103 @@ func TestDel(t *testing.T) {
|
|||
t.Fatal("delete failed #2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateList(t *testing.T) {
|
||||
copy(t)
|
||||
|
||||
db, err := InitDB("./testdata2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
success := db.CreateNode("object.list2")
|
||||
if !success {
|
||||
t.Fatal("object.list2 node was not created")
|
||||
}
|
||||
|
||||
id, err := db.AddObject("object.list2")
|
||||
fmt.Println("Added object to object.list2 id:", id)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db.Save("object.list2."+id+".newKey4", "newValue4")
|
||||
val, found := db.Get("object.list2." + id + ".newKey4")
|
||||
fmt.Println(val)
|
||||
if !found || val != "newValue4" {
|
||||
t.Fatal("object.list2.0.newKey4 value wrong")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListLength(t *testing.T) {
|
||||
copy(t)
|
||||
|
||||
db, err := InitDB("./testdata2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
success := db.CreateNode("object.list3")
|
||||
if !success {
|
||||
t.Fatal("object.list3 node was not created")
|
||||
}
|
||||
|
||||
id, _ := db.AddObject("object.list3")
|
||||
db.Save("object.list3."+id+".key", "value")
|
||||
id, _ = db.AddObject("object.list3")
|
||||
db.Save("object.list3."+id+".key", "value")
|
||||
id, _ = db.AddObject("object.list3")
|
||||
db.Save("object.list3."+id+".key", "value")
|
||||
id, _ = db.AddObject("object.list3")
|
||||
db.Save("object.list3."+id+".key", "value")
|
||||
|
||||
node, _ := db.GetNode("object.list3")
|
||||
node.FixType()
|
||||
|
||||
fmt.Println(db.GetNode("object.list3"))
|
||||
|
||||
length, err := db.Length("object.list3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if length != 4 {
|
||||
t.Fatal("object.list3 length wrong")
|
||||
}
|
||||
|
||||
length, err = db.Length("object")
|
||||
if err == nil {
|
||||
t.Fatal("Error expected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRandomNode(t *testing.T) {
|
||||
copy(t)
|
||||
|
||||
db, err := InitDB("./testdata2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
success := db.CreateNode("object.list3")
|
||||
if !success {
|
||||
t.Fatal("object.list3 node was not created")
|
||||
}
|
||||
|
||||
id, _ := db.AddObject("object.list3")
|
||||
db.Save("object.list3."+id+".key", "value")
|
||||
id, _ = db.AddObject("object.list3")
|
||||
db.Save("object.list3."+id+".key", "value")
|
||||
id, _ = db.AddObject("object.list3")
|
||||
db.Save("object.list3."+id+".key", "value")
|
||||
id, _ = db.AddObject("object.list3")
|
||||
db.Save("object.list3."+id+".key", "value")
|
||||
|
||||
node, _ := db.GetNode("object.list3")
|
||||
node.FixType()
|
||||
|
||||
node, _ = db.GetRandomNode("object.list3")
|
||||
if node.Nodes["key"].Value != "value" {
|
||||
fmt.Println(node)
|
||||
t.Fatal("Random node value wrong")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue