From 8f000ad9cd4f90c626f4b92a41f289e478cdfa24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A1=9C=E8=8F=AF?= Date: Sat, 24 Jun 2023 03:52:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=95=E5=8F=8C=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 89 +++++++++ to_entangle.go => entangle.go | 24 ++- to_entangle_test.go => entangle_test.go | 6 +- toentangle._test.go | 61 ++++++ toentangle.go | 236 ++++++++++++++++++++++++ 5 files changed, 400 insertions(+), 16 deletions(-) rename to_entangle.go => entangle.go (85%) rename to_entangle_test.go => entangle_test.go (92%) create mode 100644 toentangle._test.go create mode 100644 toentangle.go diff --git a/README.md b/README.md index 907c614..ad480d6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,91 @@ # to_entangle 由go语言实现数据的双向绑定 + + +`go get github.com/InvisibleFuture/to_entangle` + + +### 双表映射 + +```go +package main + +import ( + toentangle "github.com/InvisibleFuture/to_entangle" +) + +// 创建一个 Entangle +entangle := NewToEntangle("data/test") + +// 添加一对数据, 使其双向绑定 +entangle.Add("a", "b") +entangle.Add("a", "c") + +// 获取 a 的全部绑定数据 +arr, _ := entangle.Get("a") +fmt.Println(arr) + +// 获取 b 的全部绑定数据 +arr, _ = entangle.Get("b") +fmt.Println(arr) + +// 获取 c 的全部绑定数据 +arr, _ = entangle.Get("c") +fmt.Println(arr) + +// 移除所有绑定 +entangle.Remove("a", "b") +entangle.Remove("a", "c") + +// 获取 a 的全部绑定数据 +arr, _ = entangle.Get("a") +fmt.Println(arr) + +// 获取 s 的全部绑定数据 +arr, _ = entangle.Get("s") +fmt.Println(arr) + +``` + + +### 单表映射 + +```go +package main + +import ( + toentangle "github.com/InvisibleFuture/to_entangle" +) + +// 创建一个 Entangle +entangle := NewEntangle("data/test") + +// 添加一对数据, 使其双向绑定 +entangle.Add("a", "b") +entangle.Add("a", "c") + +// 获取 a 的全部绑定数据 +arr, _ := entangle.Get("a") +fmt.Println(arr) + +// 获取 b 的全部绑定数据 +arr, _ = entangle.Get("b") +fmt.Println(arr) + +// 获取 c 的全部绑定数据 +arr, _ = entangle.Get("c") +fmt.Println(arr) + +// 移除所有绑定 +entangle.Remove("a", "b") +entangle.Remove("a", "c") + +// 获取 a 的全部绑定数据 +arr, _ = entangle.Get("a") +fmt.Println(arr) + +// 获取 s 的全部绑定数据 +arr, _ = entangle.Get("s") +fmt.Println(arr) + +``` diff --git a/to_entangle.go b/entangle.go similarity index 85% rename from to_entangle.go rename to entangle.go index 548f504..1e3c125 100644 --- a/to_entangle.go +++ b/entangle.go @@ -7,19 +7,19 @@ import ( leveldb "github.com/syndtr/goleveldb/leveldb" ) +// 单表之内的双向绑定 // 1. 存入一对数据, 使其双向绑定 // 2. 移除一对数据, 使其双向解绑 // 3. 移除一个数据, 使其全部解绑 // 4. 获取一个数据的全部绑定数据 -type ToEntangle struct { +type Entangle struct { lock sync.Mutex db *leveldb.DB } // Get 获取一个数据的全部绑定数据 -func (t *ToEntangle) Get(a string) (arr []string, err error) { - // 获取 a 的数据, 如果不存在, 则直接返回空数组 +func (t *Entangle) Get(a string) (arr []string, err error) { data, err := t.db.Get([]byte(a), nil) if err != nil { if err == leveldb.ErrNotFound { @@ -27,8 +27,6 @@ func (t *ToEntangle) Get(a string) (arr []string, err error) { } return nil, err } - - // data 为json字符串的数组, 解码后返回 err = jsoniter.Unmarshal(data, &arr) if err != nil { return nil, err @@ -37,7 +35,7 @@ func (t *ToEntangle) Get(a string) (arr []string, err error) { } // Add 添加一对数据, 使其双向绑定 -func (t *ToEntangle) Add(a string, b string) (err error) { +func (t *Entangle) Add(a string, b string) (err error) { t.lock.Lock() defer t.lock.Unlock() @@ -90,7 +88,7 @@ func (t *ToEntangle) Add(a string, b string) (err error) { } // 单向解绑 -func (t *ToEntangle) remove_item(a string, b string) (err error) { +func (t *Entangle) remove_item(a string, b string) (err error) { // 获取 a 的数据, 如果不存在, 则直接返回 data, err := t.db.Get([]byte(a), nil) if err != nil { @@ -126,7 +124,7 @@ func (t *ToEntangle) remove_item(a string, b string) (err error) { } // Remove 移除一对数据, 使其双向解绑 -func (t *ToEntangle) Remove(a string, b string) (err error) { +func (t *Entangle) Remove(a string, b string) (err error) { t.lock.Lock() defer t.lock.Unlock() @@ -141,7 +139,7 @@ func (t *ToEntangle) Remove(a string, b string) (err error) { } // RemoveAll 移除一个数据, 使其全部解绑 -func (t *ToEntangle) RemoveAll(a string) (err error) { +func (t *Entangle) RemoveAll(a string) (err error) { t.lock.Lock() defer t.lock.Unlock() @@ -174,18 +172,18 @@ func (t *ToEntangle) RemoveAll(a string) (err error) { return nil } -// New 创建一个 ToEntangle -func New(path string) *ToEntangle { +// New 创建一个 Entangle +func NewEntangle(path string) *Entangle { db, err := leveldb.OpenFile(path, nil) if err != nil { panic(err) } - return &ToEntangle{ + return &Entangle{ db: db, } } // Close 关闭 leveldb -func (t *ToEntangle) Close() { +func (t *Entangle) Close() { t.db.Close() } diff --git a/to_entangle_test.go b/entangle_test.go similarity index 92% rename from to_entangle_test.go rename to entangle_test.go index 50eadaf..b257277 100644 --- a/to_entangle_test.go +++ b/entangle_test.go @@ -6,9 +6,9 @@ import ( "testing" ) -func TestToEntangle_Get(t *testing.T) { - // 创建一个 ToEntangle - entangle := New("data") +func TestEntangle_Get(t *testing.T) { + // 创建一个 Entangle + entangle := NewEntangle("data") // 添加一对数据, 使其双向绑定 entangle.Add("a", "b") diff --git a/toentangle._test.go b/toentangle._test.go new file mode 100644 index 0000000..524300f --- /dev/null +++ b/toentangle._test.go @@ -0,0 +1,61 @@ +package toentangle + +import ( + "fmt" + "os" + "testing" +) + +func TestToEntangle_Get(t *testing.T) { + // 创建一个 ToEntangle + toentangle := NewToEntangle("data") + + // 添加一对数据, 使其双向绑定 + toentangle.Add("a", "b") + toentangle.Add("a", "c") + + // 获取 a 的全部绑定数据 + arr, _ := toentangle.GetA("a") + fmt.Println(arr) + if len(arr) != 2 { + t.Errorf("GetA(\"a\") = %v; want [\"b\", \"c\"]", arr) + } + + // 获取 b 的全部绑定数据 + arr, _ = toentangle.GetB("b") + fmt.Println(arr) + if len(arr) != 1 || arr[0] != "a" { + t.Errorf("GetB(\"b\") = %v; want [\"a\"]", arr) + } + + // 获取 c 的全部绑定数据 + arr, _ = toentangle.GetB("c") + fmt.Println(arr) + if len(arr) != 1 || arr[0] != "a" { + t.Errorf("GetB(\"c\") = %v; want [\"a\"]", arr) + } + + // 移除所有绑定 + toentangle.Remove("a", "b") + toentangle.Remove("a", "c") + + // 获取 a 的全部绑定数据 + arr, _ = toentangle.GetA("a") + fmt.Println(arr) + if len(arr) != 0 { + t.Errorf("GetA(\"a\") = %v; want []", arr) + } + + // 获取 s 的全部绑定数据 + arr, _ = toentangle.GetB("s") + fmt.Println(arr) + if len(arr) != 0 { + t.Errorf("GetB(\"s\") = %v; want []", arr) + } + + // 清理 leveldb + toentangle.Close() + + // 删除 data 文件夹 + os.RemoveAll("data") +} diff --git a/toentangle.go b/toentangle.go new file mode 100644 index 0000000..ae4ca38 --- /dev/null +++ b/toentangle.go @@ -0,0 +1,236 @@ +package toentangle + +import ( + "sync" + + jsoniter "github.com/json-iterator/go" + leveldb "github.com/syndtr/goleveldb/leveldb" +) + +// 两表之间的双向绑定 +// 1. 存入一对数据, 使其双向绑定 +// 2. 移除一对数据, 使其双向解绑 +// 3. 移除一个数据, 使其全部解绑 +// 4. 获取一个数据的全部绑定数据 + +type ToEntangle struct { + lock sync.Mutex + dba *leveldb.DB + dbb *leveldb.DB +} + +// GetA 获取一个数据的全部绑定数据 +func (t *ToEntangle) GetA(a string) (arr []string, err error) { + // 获取 a 的数据, 如果不存在, 则直接返回空数组 + data, err := t.dba.Get([]byte(a), nil) + if err != nil { + if err == leveldb.ErrNotFound { + return []string{}, nil + } + return nil, err + } + + // data 为json字符串的数组, 解码后返回 + err = jsoniter.Unmarshal(data, &arr) + if err != nil { + return nil, err + } + return arr, nil +} + +// GetB 获取一个数据的全部绑定数据 +func (t *ToEntangle) GetB(b string) (arr []string, err error) { + // 获取 b 的数据, 如果不存在, 则直接返回空数组 + data, err := t.dbb.Get([]byte(b), nil) + if err != nil { + if err == leveldb.ErrNotFound { + return []string{}, nil + } + return nil, err + } + err = jsoniter.Unmarshal(data, &arr) + if err != nil { + return nil, err + } + return arr, nil +} + +// Add 添加一对数据, 使其双向绑定 +func (t *ToEntangle) Add(a string, b string) (err error) { + t.lock.Lock() + defer t.lock.Unlock() + + var set_data = func(a string, b string, db *leveldb.DB) (err error) { + data, err := db.Get([]byte(a), nil) + if err != nil { + if err == leveldb.ErrNotFound { + data, _ = jsoniter.Marshal([]string{b}) + return db.Put([]byte(a), data, nil) + } + return err + } + // data 为json字符串的数组, 解码后添加 b + var arr []string + err = jsoniter.Unmarshal(data, &arr) + if err != nil { + return err + } + // 转换为 map, 添加 b, 以去重 + m := make(map[string]bool) + for _, v := range arr { + m[v] = true + } + m[b] = true + + // 转换回数组 + arr = make([]string, 0, len(m)) + for k := range m { + arr = append(arr, k) + } + + // 编码后存入 + data, _ = jsoniter.Marshal(arr) + return db.Put([]byte(a), data, nil) + } + + // 设置 a 的数据 + err = set_data(a, b, t.dba) + if err != nil { + return err + } + + // 设置 b 的数据 + return set_data(b, a, t.dbb) +} + +// 单向解绑 +func (t *ToEntangle) remove(a string, b string, db *leveldb.DB) (err error) { + data, err := db.Get([]byte(a), nil) + if err != nil { + if err == leveldb.ErrNotFound { + return nil + } + return err + } + + // data 为json字符串的数组, 解码后删除 b + var arr []string + err = jsoniter.Unmarshal(data, &arr) + if err != nil { + return err + } + + // 转换为 map, 删除 b + m := make(map[string]bool) + for _, v := range arr { + m[v] = true + } + delete(m, b) + + // 转换回数组 + arr = make([]string, 0, len(m)) + for k := range m { + arr = append(arr, k) + } + + // 编码后存入 + data, _ = jsoniter.Marshal(arr) + return db.Put([]byte(a), data, nil) +} + +// Remove 移除一对数据, 使其双向解绑 +func (t *ToEntangle) Remove(a string, b string) (err error) { + t.lock.Lock() + defer t.lock.Unlock() + + // 移除 a 的数据 + err = t.remove(a, b, t.dba) + if err != nil { + return err + } + + // 移除 b 的数据 + return t.remove(b, a, t.dbb) +} + +// RemoveA 移除一个数据, 使其全部解绑 +func (t *ToEntangle) RemoveA(a string) (err error) { + t.lock.Lock() + defer t.lock.Unlock() + + // 获取 a 的数据 + data, err := t.dba.Get([]byte(a), nil) + if err != nil { + if err == leveldb.ErrNotFound { + return nil + } + return err + } + + // data 为json字符串的数组, 解码后删除全部数据 + var arr []string + err = jsoniter.Unmarshal(data, &arr) + if err != nil { + return err + } + + // 删除全部数据 + for _, v := range arr { + err = t.remove(a, v, t.dbb) + if err != nil { + return err + } + } + + // 删除 a 的数据 + return t.dba.Delete([]byte(a), nil) +} + +// RemoveB 移除一个数据, 使其全部解绑 +func (t *ToEntangle) RemoveB(b string) (err error) { + t.lock.Lock() + defer t.lock.Unlock() + + // 获取 b 的数据 + data, err := t.dbb.Get([]byte(b), nil) + if err != nil { + if err == leveldb.ErrNotFound { + return nil + } + return err + } + + // data 为json字符串的数组, 解码后删除全部数据 + var arr []string + err = jsoniter.Unmarshal(data, &arr) + if err != nil { + return err + } + + // 删除全部数据 + for _, v := range arr { + err = t.remove(b, v, t.dba) + if err != nil { + return err + } + } + + // 删除 b 的数据 + return t.dbb.Delete([]byte(b), nil) +} + +// New 创建一个双向绑定 ToEntangle +func NewToEntangle(path string) *ToEntangle { + dba, _ := leveldb.OpenFile(path+"/a", nil) + dbb, _ := leveldb.OpenFile(path+"/b", nil) + return &ToEntangle{ + dba: dba, + dbb: dbb, + } +} + +// Close 关闭 leveldb +func (t *ToEntangle) Close() { + t.dba.Close() + t.dbb.Close() +}