mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-18 21:07:44 +03:00
removed tiedot, introduced ledisdb
This commit is contained in:
parent
8ffa93780d
commit
c659b70cd0
@ -4,7 +4,7 @@ path = github.com/deluan/gosonic
|
|||||||
[deps]
|
[deps]
|
||||||
github.com/astaxie/beego = commit:92d0b9a
|
github.com/astaxie/beego = commit:92d0b9a
|
||||||
github.com/dhowden/itl = commit:35d15a3
|
github.com/dhowden/itl = commit:35d15a3
|
||||||
github.com/HouzuoGuo/tiedot = tag:3.2
|
github.com/siddontang/ledisdb = commit:713b229
|
||||||
github.com/smartystreets/goconvey = commit:899ed5a
|
github.com/smartystreets/goconvey = commit:899ed5a
|
||||||
|
|
||||||
[res]
|
[res]
|
||||||
|
@ -1,78 +1,15 @@
|
|||||||
package repositories
|
package repositories
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/HouzuoGuo/tiedot/db"
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BaseRepository struct {
|
type BaseRepository struct {
|
||||||
col *db.Col
|
key string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *BaseRepository) marshal(rec interface{}) (map[string]interface{}, error) {
|
|
||||||
// Convert to JSON...
|
|
||||||
b, err := json.Marshal(rec);
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... then convert to map
|
func (r *BaseRepository) saveOrUpdate(id string, rec interface{}) error {
|
||||||
var m map[string]interface{}
|
return hmset(r.key + "_id_" + id, rec)
|
||||||
err = json.Unmarshal(b, &m)
|
|
||||||
return m, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r*BaseRepository) query(q string, a ...interface{}) (map[int]struct{}, error) {
|
|
||||||
q = fmt.Sprintf(q, a)
|
|
||||||
|
|
||||||
var query interface{}
|
|
||||||
json.Unmarshal([]byte(q), &query)
|
|
||||||
|
|
||||||
queryResult := make(map[int]struct{})
|
|
||||||
|
|
||||||
err := db.EvalQuery(query, r.col, &queryResult)
|
|
||||||
if err != nil {
|
|
||||||
beego.Warn("Error '%s' - query='%s'", q, err)
|
|
||||||
}
|
|
||||||
return queryResult, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r*BaseRepository) queryFirstKey(q string, a ...interface{}) (int, error) {
|
|
||||||
result, err := r.query(q, a)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
for key, _ := range result {
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BaseRepository) saveOrUpdate(rec interface{}) error {
|
|
||||||
m, err := r.marshal(rec)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
docId, err := r.queryFirstKey(`{"in": ["Id"], "eq": "%s", "limit": 1}`, m["Id"])
|
|
||||||
if docId == 0 {
|
|
||||||
_, err = r.col.Insert(m)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = r.col.Update(docId, m)
|
|
||||||
if err != nil {
|
|
||||||
beego.Warn("Error updating %s[%d]: %s", r.col, docId, err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *BaseRepository) Dump() {
|
func (r *BaseRepository) Dump() {
|
||||||
r.col.ForEachDoc(func(id int, docContent []byte) (willMoveOn bool) {
|
|
||||||
beego.Debug("Document", id, "=", string(docContent))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
package repositories
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/HouzuoGuo/tiedot/db"
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_dbInstance *db.DB
|
|
||||||
once sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
func createCollection(name string, ix ...interface{}) *db.Col {
|
|
||||||
log := false
|
|
||||||
if dbInstance().Use(name) == nil {
|
|
||||||
if err := dbInstance().Create(name); err != nil {
|
|
||||||
beego.Error(err)
|
|
||||||
}
|
|
||||||
log = true
|
|
||||||
}
|
|
||||||
|
|
||||||
col := dbInstance().Use(name)
|
|
||||||
|
|
||||||
createIndex(col, []string{"Id"}, log)
|
|
||||||
for _, i := range ix {
|
|
||||||
switch i := i.(type) {
|
|
||||||
case string:
|
|
||||||
createIndex(col, []string{i}, log)
|
|
||||||
case []string:
|
|
||||||
createIndex(col, i, log)
|
|
||||||
default:
|
|
||||||
beego.Error("Trying to create an Index with an invalid type: ", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return col
|
|
||||||
}
|
|
||||||
|
|
||||||
func createIndex(col *db.Col, path []string, log bool) (err error) {
|
|
||||||
if err := col.Index(path); err != nil && log {
|
|
||||||
beego.Error(path, err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbInstance() *db.DB {
|
|
||||||
once.Do(func() {
|
|
||||||
instance, err := db.OpenDB(beego.AppConfig.String("dbPath"))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
_dbInstance = instance
|
|
||||||
})
|
|
||||||
return _dbInstance
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package repositories
|
|
||||||
|
|
||||||
import (
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
|
||||||
_ "github.com/deluan/gosonic/tests"
|
|
||||||
"testing"
|
|
||||||
"github.com/deluan/gosonic/tests"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
testCollectionName = "TestCollection"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCreateCollection(t *testing.T) {
|
|
||||||
tests.Init(t, true)
|
|
||||||
|
|
||||||
Convey("Given an empty DB", t, func() {
|
|
||||||
|
|
||||||
Convey("When creating a new collection", func() {
|
|
||||||
newCol := createCollection(testCollectionName)
|
|
||||||
|
|
||||||
Convey("Then it should create the collection", func() {
|
|
||||||
So(dbInstance().Use(testCollectionName), ShouldNotBeNil)
|
|
||||||
})
|
|
||||||
Convey("And it should create a default index on Id", func() {
|
|
||||||
allIndexes := newCol.AllIndexes()
|
|
||||||
So(len(allIndexes), ShouldEqual, 1)
|
|
||||||
So(len(allIndexes[0]), ShouldEqual, 1)
|
|
||||||
So(allIndexes[0], ShouldContain, "Id")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("When creating a new collection with a 'Name' index", func() {
|
|
||||||
newCol := createCollection(testCollectionName, "Name")
|
|
||||||
|
|
||||||
Convey("Then it should create a default index [Id] and an index on 'Name'", func() {
|
|
||||||
listOfIndexes := newCol.AllIndexes()
|
|
||||||
|
|
||||||
So(len(listOfIndexes), ShouldEqual, 2)
|
|
||||||
|
|
||||||
var allPaths = make(map[string]bool)
|
|
||||||
for _, i := range listOfIndexes {
|
|
||||||
allPaths[i[0]] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
So(allPaths, ShouldContainKey, "Id")
|
|
||||||
So(allPaths, ShouldContainKey, "Name")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Reset(func() {
|
|
||||||
dbInstance().Drop(testCollectionName)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
49
repositories/ledis_database.go
Normal file
49
repositories/ledis_database.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
|
"github.com/siddontang/ledisdb/config"
|
||||||
|
"github.com/deluan/gosonic/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_dbInstance *ledis.DB
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func db() *ledis.DB {
|
||||||
|
once.Do(func() {
|
||||||
|
config := config.NewConfigDefault()
|
||||||
|
config.DataDir = beego.AppConfig.String("dbPath")
|
||||||
|
l, _ := ledis.Open(config)
|
||||||
|
instance, err := l.Select(0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
_dbInstance = instance
|
||||||
|
})
|
||||||
|
return _dbInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
func hmset(key string, data interface{}) error {
|
||||||
|
h, err := utils.Flatten(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var fvList = make([]ledis.FVPair, len(h))
|
||||||
|
i := 0
|
||||||
|
for f, v := range h {
|
||||||
|
fvList[i].Field = []byte(f)
|
||||||
|
fvList[i].Value, _ = json.Marshal(v)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return db().HMset([]byte(key), fvList...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hset(key, field, value string) error {
|
||||||
|
_, err := db().HSet([]byte(key), []byte(field), []byte(value))
|
||||||
|
return err
|
||||||
|
}
|
@ -12,7 +12,7 @@ type MediaFile struct {
|
|||||||
|
|
||||||
func NewMediaFileRepository() *MediaFile {
|
func NewMediaFileRepository() *MediaFile {
|
||||||
r := &MediaFile{}
|
r := &MediaFile{}
|
||||||
r.col = createCollection("MediaFiles")
|
r.key = "mediafile"
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,5 +20,5 @@ func (r *MediaFile) Add(m *models.MediaFile) error {
|
|||||||
if m.Id == "" {
|
if m.Id == "" {
|
||||||
m.Id = fmt.Sprintf("%x", md5.Sum([]byte(m.Path)))
|
m.Id = fmt.Sprintf("%x", md5.Sum([]byte(m.Path)))
|
||||||
}
|
}
|
||||||
return r.saveOrUpdate(m)
|
return r.saveOrUpdate(m.Id, m)
|
||||||
}
|
}
|
130
utils/flat.go
Normal file
130
utils/flat.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// Adapted from https://github.com/nytlabs/gojsonexplode
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
const delimiter = "."
|
||||||
|
|
||||||
|
func explodeList(l []interface{}, parent string, delimiter string) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
var key string
|
||||||
|
j := make(map[string]interface{})
|
||||||
|
for k, i := range l {
|
||||||
|
if len(parent) > 0 {
|
||||||
|
key = parent + delimiter + strconv.Itoa(k)
|
||||||
|
} else {
|
||||||
|
key = strconv.Itoa(k)
|
||||||
|
}
|
||||||
|
switch v := i.(type) {
|
||||||
|
case nil:
|
||||||
|
j[key] = v
|
||||||
|
case int:
|
||||||
|
j[key] = v
|
||||||
|
case float64:
|
||||||
|
j[key] = v
|
||||||
|
case string:
|
||||||
|
j[key] = v
|
||||||
|
case bool:
|
||||||
|
j[key] = v
|
||||||
|
case []interface{}:
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
out, err = explodeList(v, key, delimiter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for newkey, value := range out {
|
||||||
|
j[newkey] = value
|
||||||
|
}
|
||||||
|
case map[string]interface{}:
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
out, err = explodeMap(v, key, delimiter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for newkey, value := range out {
|
||||||
|
j[newkey] = value
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func explodeMap(m map[string]interface{}, parent string, delimiter string) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
j := make(map[string]interface{})
|
||||||
|
for k, i := range m {
|
||||||
|
if len(parent) > 0 {
|
||||||
|
k = parent + delimiter + k
|
||||||
|
}
|
||||||
|
switch v := i.(type) {
|
||||||
|
case nil:
|
||||||
|
j[k] = v
|
||||||
|
case int:
|
||||||
|
j[k] = v
|
||||||
|
case float64:
|
||||||
|
j[k] = v
|
||||||
|
case string:
|
||||||
|
j[k] = v
|
||||||
|
case bool:
|
||||||
|
j[k] = v
|
||||||
|
case []interface{}:
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
out, err = explodeList(v, k, delimiter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for key, value := range out {
|
||||||
|
j[key] = value
|
||||||
|
}
|
||||||
|
case map[string]interface{}:
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
out, err = explodeMap(v, k, delimiter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for key, value := range out {
|
||||||
|
j[key] = value
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
//nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlattenMap(input map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var flattened map[string]interface{}
|
||||||
|
var err error
|
||||||
|
flattened, err = explodeMap(input, "", delimiter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return flattened, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal(rec interface{}) (map[string]interface{}, error) {
|
||||||
|
// Convert to JSON...
|
||||||
|
b, err := json.Marshal(rec);
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... then convert to map
|
||||||
|
var m map[string]interface{}
|
||||||
|
err = json.Unmarshal(b, &m)
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flatten(input interface{}) (map[string]interface{}, error) {
|
||||||
|
m, err := marshal(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return FlattenMap(m)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user