kv2doc

package module
v0.0.0-...-867e29d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 15, 2025 License: MIT Imports: 10 Imported by: 0

README

kv2doc

一个嵌入式文档数据库,基于 Go + BoltDB + Expr-lang 实现

实现功能
  • 支持基本的表结构及文档数据插入/更新/删除/批量增删改操作。
  • 支持索引维护及查询(遵循最左前缀原则)。
  • 支持简单的条件查询与复杂的嵌套查询。
  • 支持列表查询(排序+分页)与滚动查询。

使用示例
  • 安装
go get github.com/dpwgc/kv2doc
  • 代码示例
package main

import (
	"fmt"
	"github.com/dpwgc/kv2doc"
)

func main() {

	// 新建数据库 demo.db
	db, _ := kv2doc.NewDB("demo.db")

	// 往 test_table 表中插入2条数据(无需建表,插入数据时会自动建表,同时为每一个字段都建立索引)
	_, _ = db.Add("test_table", kv2doc.Doc{
		"title": "hello world 1",
		"type":  "1",
	})
	id, _ := db.Add("test_table", kv2doc.Doc{
		"title": "hello world 2",
		"type":  "2",
	})

	// 更新第2条数据,新增一个 color 字段
	_ = db.Edit("test_table", id, kv2doc.Doc{
		"title": "hello world 2",
		"type":  "2",
		"color": "red",
	})

	// 查询文档,筛选条件:title 以 hello 为前缀, type 要大于 0 且 存在 color 字段,结果集按主键ID排序后,取前10条返回
	// 使用 Eq 或 LeftLike 进行查询时,会走最左前缀索引,其他查询方法走全表扫描
	documents, _ := db.Query("test_table").
		LeftLike("title", "hello").
		Must(kv2doc.Expr().Gt("type", "0").Exist("color")).
		Desc("_id").
		Limit(0, 10).
		List()

	// 打印查询结果
	for _, v := range documents {
		fmt.Println(v.ToJson())
	}

	// 查看Query执行计划
	explain := db.Query("test_table").
		LeftLike("title", "hello").
		Must(kv2doc.Expr().Gt("type", "0").Exist("color")).
		Explain()

	// 具体执行逻辑
	fmt.Println("expr:", explain.Expr)

	// 选择了哪个索引
	fmt.Println("index:", explain.Index)

	// 删除表
	_ = db.Drop("test_table")
}

函数说明
名称 功能
kv2doc.NewDB 创建/打开一个数据库
kv2doc.ByStore 创建/打开一个数据库(自定义存储引擎)
db.Add 新增文档(表不存在时自动建表)
db.Edit 编辑文档
db.Delete 删除文档
db.Bulk 批量操作(增删改)
db.Drop 删除表
db.Query 新建查询
Query.Eq 等于
Query.Ne 不等于
Query.Gt 大于
Query.Gte 大于等于
Query.Lt 小于
Query.Lte 小于等于
Query.In 包含
Query.NotIn 不包含
Query.Like 含有
Query.LeftLike 相同前缀
Query.RightLike 相同后缀
Query.Exist 存在
Query.NotExist 不存在
Query.Must 交集语句
Query.Should 并集语句
Query.Asc 正序
Query.Desc 倒序
Query.Limit 分页
Query.One 返回一个文档
Query.List 返回多个文档
Query.Count 返回文档数量
Query.Scroll 滚动查询文档
Query.Explain 查看执行计划

存储实现原理
假设有一个文档,Json 格式如下( _id 为文档主键,使用 BoltDB 的 NextSequence 方法获取)
{
  "_id": "123",
  "title": "hello world",
  "type": "1",
  "color": "red"
}
在保存此文档时,会将该文档的非主键字段拆解成索引,分别存进 BoltDB 键值对中,Key 是字段名 + 字段值 + 主键 id,Value 是主键 id
key value
f/_id/123/123 123
f/title/hello world/123 123
f/type/1/123 123
f/color/red/123 123
上述表格展示的是字段索引( key 以 f 前缀开头),只有文档 id,没有文档内容。而真正的文档内容,保存在主键 Key 下( key 以 p 前缀开头)
key value
p/_id/123 { "_id": "123", "title": "hello world", "type": "1", "color": "red" }

查询实现原理
查询情况可分为两种:索引扫描 or 全表扫描
索引扫描:
  • 如果使用了 Eq(等于)、 LeftLike(前缀相同)或者 In(数组内必须要有共同前缀才能走索引),会按最左前缀原则匹配索引

  • 例如:执行 LeftLike("title", "hello").Gt("type", "1"),会先利用 BoltDB 的 Cursor 遍历功能扫描所有前缀为 f/title/hello 的 key

  • 然后再根据该索引扫描的结果作其他条件筛选(先根据字段索引 value 中的主键 id 找到文档内容,再判断文档中的 type 字段是否大于 1)

全表扫描:
  • 当全表扫描时,会在 BoltDB 中扫描所有前缀为 p 的 key(即所有存放文档内容的主键 key),然后再根据文档内容逐条匹配

自定义存储实现
可以使用其他带事务功能的键值数据库来充当存储引擎,只需实现下述接口(务必确保所有操作方法都有事务保障),并调用 ByStore 方法生成数据库实例即可
type Store interface {
    CreateTable(table string) (err error)
    DropTable(table string) (err error)
    SetKV(table string, kvs []KV) (err error)
    GetKV(table, key string) (kv KV, err error)
    ScanKV(table, prefix string, handle func(key string, value []byte) bool) (err error)
    NextID(table string) (id string, err error)
}
db := kv2doc.ByStore(rocketStore)
db := kv2doc.ByStore(etcdStore)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Sort

func Sort(rows []Doc, compare func(l, r Doc) bool)

Types

type Bulk

type Bulk struct {
	// contains filtered or unexported fields
}

func (*Bulk) Add

func (c *Bulk) Add(doc Doc) *Bulk

func (*Bulk) Delete

func (c *Bulk) Delete(id string) *Bulk

func (*Bulk) Edit

func (c *Bulk) Edit(id string, doc Doc) *Bulk

func (*Bulk) Exec

func (c *Bulk) Exec() (ids []string, err error)

type DB

type DB struct {
	// contains filtered or unexported fields
}

func ByStore

func ByStore(store store.Store) *DB

ByStore 开启一个数据库(自定义底层存储引擎实现)

func NewDB

func NewDB(path string) (*DB, error)

NewDB 开启一个数据库,不存在时自动建库,底层基于 BoltDB

func (*DB) Add

func (c *DB) Add(table string, doc Doc) (id string, err error)

Add 在指定表中插入文档记录(表不存在时自动建表)

func (*DB) Bulk

func (c *DB) Bulk(table string) *Bulk

Bulk 批量操作

func (*DB) Delete

func (c *DB) Delete(table string, id string) (err error)

Delete 删除指定表中的指定文档记录

func (*DB) Drop

func (c *DB) Drop(table string) error

Drop 删除指定表

func (*DB) Edit

func (c *DB) Edit(table string, id string, doc Doc) (err error)

Edit 更新指定表中的指定文档记录 id 为文档主键 ID,在 Add 文档记录时会返回

func (*DB) Query

func (c *DB) Query(table string) *Query

Query 查询文档

type Doc

type Doc map[string]string

func (Doc) CreatedAt

func (c Doc) CreatedAt() string

func (Doc) CreatedMill

func (c Doc) CreatedMill() int64

func (Doc) CreatedTime

func (c Doc) CreatedTime() time.Time

func (Doc) Fields

func (c Doc) Fields() []string

func (Doc) FromBytes

func (c Doc) FromBytes(src []byte) Doc

func (Doc) FromJson

func (c Doc) FromJson(s string) Doc

func (Doc) HasField

func (c Doc) HasField(key string) bool

func (Doc) ID

func (c Doc) ID() string

func (Doc) IsEmpty

func (c Doc) IsEmpty() bool

func (Doc) IsValid

func (c Doc) IsValid() bool

func (Doc) NumberID

func (c Doc) NumberID() int64

func (Doc) ToBytes

func (c Doc) ToBytes() []byte

func (Doc) ToJson

func (c Doc) ToJson() string

func (Doc) UpdatedAt

func (c Doc) UpdatedAt() string

func (Doc) UpdatedMill

func (c Doc) UpdatedMill() int64

func (Doc) UpdatedTime

func (c Doc) UpdatedTime() time.Time

func (Doc) UserFields

func (c Doc) UserFields() []string

func (Doc) UserValues

func (c Doc) UserValues() []string

func (Doc) Values

func (c Doc) Values() []string

type Explain

type Explain struct {
	Expr  string
	Index Index
}

type Index

type Index struct {
	// contains filtered or unexported fields
}

type Parser

type Parser struct {
}

func NewParser

func NewParser() *Parser

func (*Parser) Match

func (c *Parser) Match(code string, doc Doc) (bool, error)

type Query

type Query struct {
	// contains filtered or unexported fields
}

func Expr

func Expr() *Query

func (*Query) Asc

func (c *Query) Asc(fields ...string) *Query

func (*Query) Count

func (c *Query) Count() (count int64, err error)

Count 返回文档数量

func (*Query) Desc

func (c *Query) Desc(fields ...string) *Query

func (*Query) Eq

func (c *Query) Eq(field, value string) *Query

Eq 等于

func (*Query) Exist

func (c *Query) Exist(field string) *Query

Exist 存在该字段

func (*Query) Explain

func (c *Query) Explain() Explain

Explain 执行计划

func (*Query) Gt

func (c *Query) Gt(field, value string) *Query

Gt 大于

func (*Query) Gte

func (c *Query) Gte(field, value string) *Query

Gte 大于或等于

func (*Query) In

func (c *Query) In(field string, values ...string) *Query

In 包含

func (*Query) LeftLike

func (c *Query) LeftLike(field, value string) *Query

LeftLike 模糊匹配-具有相同的前缀 此方法会走字段索引

func (*Query) Like

func (c *Query) Like(field, value string) *Query

Like 模糊匹配

func (*Query) Limit

func (c *Query) Limit(values ...int) *Query

Limit 分页方法,逻辑和 MySQL 的 Limit 相同,limit 10 或 limit 0,10

func (*Query) List

func (c *Query) List() (docs []Doc, err error)

List 返回多个文档

func (*Query) Lt

func (c *Query) Lt(field, value string) *Query

Lt 小于

func (*Query) Lte

func (c *Query) Lte(field, value string) *Query

Lte 小于或等于

func (*Query) Must

func (c *Query) Must(sc *Query) *Query

Must 交集拼接

func (*Query) Ne

func (c *Query) Ne(field, value string) *Query

Ne 不等于

func (*Query) NotExist

func (c *Query) NotExist(field string) *Query

NotExist 不存在该字段

func (*Query) NotIn

func (c *Query) NotIn(field string, values ...string) *Query

NotIn 不包含

func (*Query) One

func (c *Query) One() (doc Doc, err error)

One 查询单个文档

func (*Query) RightLike

func (c *Query) RightLike(field, value string) *Query

RightLike 模糊匹配-具有相同的后缀

func (*Query) Scroll

func (c *Query) Scroll(fn func(doc Doc) bool) error

Scroll 滚动查询

func (*Query) Should

func (c *Query) Should(sc *Query) *Query

Should 并集拼接

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL