golang无依赖事务型图数据库插件eliasdb支持REST API和类SQL查询

Golang无依赖事务型图数据库插件EliasDB支持REST API和类SQL查询

EliasDB简介

EliasDB是一个基于图的数据库,旨在为需要将数据存储为图的项目提供轻量级解决方案。

EliasDB Logo

主要特性

  • 基于支持事务和纯内存存储的自定义键值存储构建
  • 数据存储在通过边连接的节点(键值对象)中
  • 存储的图可以通过分区进行分离
  • 存储的图支持级联删除 - 删除一个节点及其所有"子节点"
  • 所有存储的数据都被索引,可以通过全文短语搜索快速搜索
  • EliasDB有一个GraphQL接口,可用于存储和检索数据
  • 对于更复杂的查询,EliasDB有自己的查询语言EQL,具有类似SQL的语法
  • 包含一个脚本解释器,用于定义数据库操作的替代操作或编写后端逻辑
  • 完全用Go编写,仅使用gorilla/websocket来支持GraphQL订阅的WebSocket
  • 可以作为嵌入式数据库使用,也可以作为独立应用程序使用
  • 作为独立应用程序使用时,内置HTTPS网络服务器,提供用户管理、REST API和基本文件服务器
  • 作为嵌入式数据库使用时,支持带有回滚的事务、数据迭代和基于规则的一致性管理

快速入门(独立应用程序)

下载预编译的Windows(win64)或Linux(amd64)包,解压并执行:

eliasdb server

可执行文件应自动创建3个子文件夹和一个配置文件。它应该在9090端口启动一个HTTPS服务器。要查看终端,请将浏览器指向:

https://localhost:9090/db/term.html

接受服务器的自签名证书后,您应该会看到一个Web终端。可以通过简单的CTRL+C或用一个字符覆盖eliasdb.lck文件的内容来停止EliasDB。

REST API示例

EliasDB提供了一个REST API,可以通过动态生成的swagger.json定义浏览。以下是使用REST API的示例代码:

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	// 查询节点数据
	resp, err := http.Get("https://localhost:9090/db/v1/graph/main?part=main&kind=Person")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error reading response:", err)
		return
	}

	fmt.Println("Response:", string(body))
}

EQL类SQL查询示例

EliasDB的EQL查询语言允许执行类似SQL的查询。以下是一个示例:

package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {
	// 准备EQL查询
	query := `{
		"query": "get Person where name = 'John' traverse Friend:friends:>1 where name = 'Mary'",
		"part": "main"
	}`

	// 发送查询请求
	resp, err := http.Post(
		"https://localhost:9090/db/v1/query/main",
		"application/json",
		strings.NewReader(query),
	)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer resp.Body.Close()

	// 读取响应
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error reading response:", err)
		return
	}

	fmt.Println("Query result:", string(body))
}

完整示例Demo

以下是一个完整的示例,展示如何使用EliasDB的REST API和EQL查询:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Node struct {
	Key  string            `json:"key"`
	Kind string            `json:"kind"`
	Name string            `json:"name"`
	Data map[string]string `json:"data"`
}

type Edge struct {
	Key      string `json:"key"`
	Kind     string `json:"kind"`
	From     string `json:"from"`
	To       string `json:"to"`
	FromKind string `json:"from_kind"`
	ToKind   string `json:"to_kind"`
}

func main() {
	// 1. 创建节点
	person1 := Node{
		Key:  "john",
		Kind: "Person",
		Name: "John Doe",
		Data: map[string]string{
			"age":  "30",
			"city": "New York",
		},
	}

	person2 := Node{
		Key:  "mary",
		Kind: "Person",
		Name: "Mary Smith",
		Data: map[string]string{
			"age":  "28",
			"city": "Boston",
		},
	}

	// 存储节点
	storeNode(person1)
	storeNode(person2)

	// 2. 创建边
	friendship := Edge{
		Key:      "john-mary",
		Kind:     "Friend",
		From:     "john",
		To:       "mary",
		FromKind: "Person",
		ToKind:   "Person",
	}

	// 存储边
	storeEdge(friendship)

	// 3. 执行EQL查询
	query := map[string]string{
		"query": "get Person where name = 'John Doe' traverse Friend:friends:>1 where name = 'Mary Smith'",
		"part":  "main",
	}

	queryResult := executeEQL(query)
	fmt.Println("Query Result:", queryResult)
}

func storeNode(node Node) {
	url := "https://localhost:9090/db/v1/graph/main"
	jsonData, _ := json.Marshal(node)
	
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		fmt.Println("Error storing node:", err)
		return
	}
	defer resp.Body.Close()
	
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Printf("Stored node %s: %s\n", node.Key, string(body))
}

func storeEdge(edge Edge) {
	url := "https://localhost:9090/db/v1/graph/main"
	jsonData, _ := json.Marshal(edge)
	
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		fmt.Println("Error storing edge:", err)
		return
	}
	defer resp.Body.Close()
	
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Printf("Stored edge %s: %s\n", edge.Key, string(body))
}

func executeEQL(query map[string]string) string {
	url := "https://localhost:9090/db/v1/query/main"
	jsonData, _ := json.Marshal(query)
	
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		fmt.Println("Error executing query:", err)
		return ""
	}
	defer resp.Body.Close()
	
	body, _ := ioutil.ReadAll(resp.Body)
	return string(body)
}

嵌入式使用示例

EliasDB也可以作为嵌入式数据库使用:

package main

import (
	"fmt"
	"devt.de/krotik/eliasdb/api"
	"devt.de/krotik/eliasdb/eql"
	"devt.de/krotik/eliasdb/graph"
	"devt.de/krotik/eliasdb/graph/data"
	"devt.de/krotik/eliasdb/graph/graphstorage"
)

func main() {
	// 1. 创建内存存储
	gs, err := graphstorage.NewMemoryGraphStorage("mystorage")
	if err != nil {
		panic(err)
	}

	// 2. 创建图管理器
	gm := graph.NewGraphManager(gs)

	// 3. 创建分区
	gm.CreatePartition("main")

	// 4. 添加节点和边
	node1 := data.NewGraphNode()
	node1.SetAttr("key", "john")
	node1.SetAttr("kind", "Person")
	node1.SetAttr("name", "John Doe")
	node1.SetAttr(data.NodeAttrPartition, "main")

	node2 := data.NewGraphNode()
	node2.SetAttr("key", "mary")
	node2.SetAttr("kind", "Person")
	node2.SetAttr("name", "Mary Smith")
	node2.SetAttr(data.NodeAttrPartition, "main")

	// 存储节点
	gm.StoreNode("main", node1)
	gm.StoreNode("main", node2)

	// 创建边
	edge := data.NewGraphEdge()
	edge.SetAttr("key", "john-mary")
	edge.SetAttr("kind", "Friend")
	edge.SetAttr("from", "john")
	edge.SetAttr("to", "mary")
	edge.SetAttr("fromkind", "Person")
	edge.SetAttr("tokind", "Person")
	edge.SetAttr(data.NodeAttrPartition, "main")

	// 存储边
	gm.StoreEdge("main", edge)

	// 5. 执行EQL查询
	res, err := eql.ParseAndExecute("get Person where name = 'John Doe'", gm, "main")
	if err != nil {
		panic(err)
	}

	// 6. 处理结果
	for _, row := range res.Rows {
		fmt.Println("Node:", row[0])
	}

	// 7. 启动REST API服务
	api.Start(gm, "localhost:9090")
}

总结

EliasDB是一个功能丰富的图数据库,提供:

  • 无外部依赖的轻量级解决方案
  • 支持事务操作
  • 提供REST API接口
  • 支持类SQL的EQL查询语言
  • 可作为独立服务或嵌入式数据库使用

通过上述示例代码,您可以快速开始使用EliasDB的REST API和EQL查询功能来构建图数据库应用。


更多关于golang无依赖事务型图数据库插件eliasdb支持REST API和类SQL查询的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang无依赖事务型图数据库插件eliasdb支持REST API和类SQL查询的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


EliasDB - Go语言无依赖事务型图数据库插件

EliasDB是一个用纯Go编写的轻量级图数据库,具有以下特点:

  • 无外部依赖
  • 支持ACID事务
  • 提供REST API接口
  • 支持类SQL查询语言(EQL)
  • 嵌入式设计,可作为库直接集成到应用中

基本概念

EliasDB中的数据组织为:

  • 节点(Node):表示实体,有类型和属性
  • 边(Edge):表示节点间关系,有类型和方向
  • 分区(Partition):数据逻辑分组,类似命名空间

安装

go get github.com/krotik/eliasdb

基本使用示例

1. 创建数据库实例

package main

import (
	"github.com/krotik/eliasdb/api"
	"github.com/krotik/eliasdb/eql"
	"github.com/krotik/eliasdb/graph"
	"github.com/krotik/eliasdb/graph/data"
	"github.com/krotik/eliasdb/graph/graphstorage"
)

func main() {
	// 创建内存存储
	gs := graphstorage.NewMemoryGraphStorage("mygraph")
	
	// 创建图管理器
	gm := graph.NewGraphManager(gs)
	
	// 创建分区
	gm.EnsureNodeType("main", "Person")
	gm.EnsureNodeType("main", "City")
	gm.EnsureEdgeType("main", "LivesIn")
	
	// 启动REST API服务
	api.StartServer(":9090", "/api/v1", gm, "web")
}

2. 添加数据

// 添加节点
person := data.NewGraphNode()
person.SetAttr("key", "john")
person.SetAttr("name", "John Doe")
person.SetAttr("age", 34)
person.SetAttr("kind", "Person")

city := data.NewGraphNode()
city.SetAttr("key", "ny")
city.SetAttr("name", "New York")
city.SetAttr("kind", "City")

// 添加边
edge := data.NewGraphEdge()
edge.SetAttr("key", "john_ny")
edge.SetAttr("kind", "LivesIn")
edge.SetAttr("from", "john")
edge.SetAttr("to", "ny")

// 事务方式写入
gm.StoreNode("main", person)
gm.StoreNode("main", city)
gm.StoreEdge("main", edge)

3. 使用EQL查询

// 查询所有名为John的人
res, err := eql.ParseAndExecute("get Person where name = 'John Doe'", gm, "main")

// 查询John住在哪里
res, err := eql.ParseAndExecute(`
    traverse end:City from Person:john 
    via LivesIn:john_ny
`, gm, "main")

4. REST API示例

EliasDB提供了完整的REST API接口:

  • 查询节点: GET /api/v1/query/main?q=get Person where name = 'John Doe'
  • 添加节点: POST /api/v1/node/main/Person
    {
      "key": "mary",
      "name": "Mary Smith",
      "age": 28
    }
    
  • 添加关系: POST /api/v1/edge/main/LivesIn
    {
      "key": "mary_ny",
      "from": "mary",
      "to": "ny"
    }
    

高级特性

1. 事务处理

tx := gm.NewGraphTraversal("main")
defer tx.Rollback()

// 在事务中操作
tx.StoreNode(person)
tx.StoreEdge(edge)

// 提交事务
if err := tx.Commit(); err != nil {
    // 处理错误
}

2. 全文搜索

// 创建全文索引
gm.EnsureNodeFulltextIndex("main", "Person", "name")

// 使用全文搜索查询
res, err := eql.ParseAndExecute(`
    get Person where fulltext(name) = 'John'
`, gm, "main")

3. 复杂遍历查询

// 查找John的朋友的朋友
res, err := eql.ParseAndExecute(`
    traverse depth:2 from Person:john 
    via Friend:*
`, gm, "main")

性能优化建议

  1. 为常用查询条件创建索引:

    gm.EnsureNodeIndex("main", "Person", "age")
    
  2. 对大图使用分页查询:

    res, err := eql.ParseAndExecute(`
        get Person limit 10 offset 20
    `, gm, "main")
    
  3. 考虑使用磁盘存储替代内存存储:

    gs := graphstorage.NewDiskGraphStorage("data", "mygraph")
    

适用场景

EliasDB适合以下场景:

  • 需要嵌入式图数据库的Go应用
  • 中小规模图数据(百万节点级别)
  • 需要事务支持的场景
  • 需要简单REST接口的项目

对于超大规模图数据,可能需要考虑Neo4j等专业图数据库解决方案。

EliasDB以其简洁性和零依赖特性,成为Go生态中图数据库的一个轻量级选择。

回到顶部