Feat: 实现知识图谱数据库的检索方法,并对外提供接口
This commit is contained in:
@@ -40,7 +40,10 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Load() (*Config, error) {
|
func Load() (*Config, error) {
|
||||||
if err := godotenv.Load("../config/.env"); err != nil {
|
var err error
|
||||||
|
err = godotenv.Load("../config/.env")
|
||||||
|
err = godotenv.Load("./config/.env")
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error loading .env file: %w", err)
|
return nil, fmt.Errorf("error loading .env file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,255 +0,0 @@
|
|||||||
{
|
|
||||||
"nodes": [
|
|
||||||
{
|
|
||||||
"id": "1",
|
|
||||||
"label": "人工智能",
|
|
||||||
"type": "概念",
|
|
||||||
"properties": {
|
|
||||||
"importance": 1,
|
|
||||||
"description": "计算机科学的一个分支,致力于创建能够执行通常需要人类智能的任务的机器",
|
|
||||||
"aliases": ["机器智能", "AI"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2",
|
|
||||||
"label": "机器学习",
|
|
||||||
"type": "概念",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.9,
|
|
||||||
"description": "人工智能的一个子领域,让计算机系统能够从数据中学习和改进"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3",
|
|
||||||
"label": "深度学习",
|
|
||||||
"type": "概念",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.8,
|
|
||||||
"description": "基于人工神经网络的机器学习方法"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "4",
|
|
||||||
"label": "神经网络",
|
|
||||||
"type": "概念",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.7,
|
|
||||||
"description": "受人脑启发的计算模型"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "5",
|
|
||||||
"label": "TensorFlow",
|
|
||||||
"type": "工具",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.6,
|
|
||||||
"language": "Python, C++",
|
|
||||||
"author": "Google"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "6",
|
|
||||||
"label": "PyTorch",
|
|
||||||
"type": "工具",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.6,
|
|
||||||
"language": "Python",
|
|
||||||
"author": "Meta"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "7",
|
|
||||||
"label": "自然语言处理",
|
|
||||||
"type": "应用",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.7,
|
|
||||||
"description": "处理和理解人类语言的技术"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "8",
|
|
||||||
"label": "计算机视觉",
|
|
||||||
"type": "应用",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.7,
|
|
||||||
"description": "让计算机理解和解析视觉信息"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "9",
|
|
||||||
"label": "强化学习",
|
|
||||||
"type": "概念",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.6,
|
|
||||||
"description": "通过与环境交互来学习最优策略"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "10",
|
|
||||||
"label": "监督学习",
|
|
||||||
"type": "概念",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.5,
|
|
||||||
"description": "使用标记数据进行学习"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "11",
|
|
||||||
"label": "GPT",
|
|
||||||
"type": "应用",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.8,
|
|
||||||
"author": "OpenAI",
|
|
||||||
"description": "Generative Pre-trained Transformer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "12",
|
|
||||||
"label": "CNN",
|
|
||||||
"type": "概念",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.6,
|
|
||||||
"description": "卷积神经网络,用于处理网格状数据"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "13",
|
|
||||||
"label": "RNN",
|
|
||||||
"type": "概念",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.6,
|
|
||||||
"description": "循环神经网络,用于处理序列数据"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "14",
|
|
||||||
"label": "数据挖掘",
|
|
||||||
"type": "应用",
|
|
||||||
"properties": {
|
|
||||||
"importance": 0.5,
|
|
||||||
"description": "从大量数据中发现模式和知识"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"edges": [
|
|
||||||
{
|
|
||||||
"id": "e1",
|
|
||||||
"source": "1",
|
|
||||||
"target": "2",
|
|
||||||
"label": "包含",
|
|
||||||
"type": "关系"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e2",
|
|
||||||
"source": "2",
|
|
||||||
"target": "3",
|
|
||||||
"label": "子领域",
|
|
||||||
"type": "关系"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e3",
|
|
||||||
"source": "3",
|
|
||||||
"target": "4",
|
|
||||||
"label": "基于",
|
|
||||||
"type": "技术依赖"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e4",
|
|
||||||
"source": "3",
|
|
||||||
"target": "5",
|
|
||||||
"label": "框架",
|
|
||||||
"type": "工具关系"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e5",
|
|
||||||
"source": "3",
|
|
||||||
"target": "6",
|
|
||||||
"label": "框架",
|
|
||||||
"type": "工具关系"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e6",
|
|
||||||
"source": "1",
|
|
||||||
"target": "7",
|
|
||||||
"label": "应用",
|
|
||||||
"type": "应用领域"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e7",
|
|
||||||
"source": "1",
|
|
||||||
"target": "8",
|
|
||||||
"label": "应用",
|
|
||||||
"type": "应用领域"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e8",
|
|
||||||
"source": "2",
|
|
||||||
"target": "9",
|
|
||||||
"label": "包含",
|
|
||||||
"type": "关系"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e9",
|
|
||||||
"source": "2",
|
|
||||||
"target": "10",
|
|
||||||
"label": "包含",
|
|
||||||
"type": "关系"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e10",
|
|
||||||
"source": "7",
|
|
||||||
"target": "11",
|
|
||||||
"label": "技术实现",
|
|
||||||
"type": "技术依赖"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e11",
|
|
||||||
"source": "3",
|
|
||||||
"target": "12",
|
|
||||||
"label": "主要架构",
|
|
||||||
"type": "技术依赖"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e12",
|
|
||||||
"source": "3",
|
|
||||||
"target": "13",
|
|
||||||
"label": "主要架构",
|
|
||||||
"type": "技术依赖"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e13",
|
|
||||||
"source": "8",
|
|
||||||
"target": "12",
|
|
||||||
"label": "核心技术",
|
|
||||||
"type": "技术依赖"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e14",
|
|
||||||
"source": "7",
|
|
||||||
"target": "13",
|
|
||||||
"label": "核心技术",
|
|
||||||
"type": "技术依赖"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e15",
|
|
||||||
"source": "2",
|
|
||||||
"target": "14",
|
|
||||||
"label": "相关技术",
|
|
||||||
"type": "相关"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e16",
|
|
||||||
"source": "5",
|
|
||||||
"target": "12",
|
|
||||||
"label": "支持",
|
|
||||||
"type": "技术支持"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e17",
|
|
||||||
"source": "6",
|
|
||||||
"target": "13",
|
|
||||||
"label": "支持",
|
|
||||||
"type": "技术支持"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -8,22 +8,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type GraphHandler struct {
|
type GraphHandler struct {
|
||||||
dataService *services.DataService
|
service services.GraphService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGraphHandler(dataService *services.DataService) *GraphHandler {
|
func NewGraphHandler(service services.GraphService) *GraphHandler {
|
||||||
return &GraphHandler{
|
return &GraphHandler{
|
||||||
dataService: dataService,
|
service: service,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *GraphHandler) GetGraphData(c *gin.Context) {
|
func (h *GraphHandler) GetGraphData(c *gin.Context) {
|
||||||
data := h.dataService.GetGraphData()
|
data := h.service.GetGraphData()
|
||||||
c.JSON(http.StatusOK, data)
|
c.JSON(http.StatusOK, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *GraphHandler) GetStats(c *gin.Context) {
|
func (h *GraphHandler) GetStats(c *gin.Context) {
|
||||||
stats := h.dataService.GetStats()
|
stats := h.service.GetStats()
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"stats": stats,
|
"stats": stats,
|
||||||
"timestamp": gin.H{
|
"timestamp": gin.H{
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NodeHandler struct {
|
type NodeHandler struct {
|
||||||
dataService *services.DataService
|
service services.GraphService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNodeHandler(dataService *services.DataService) *NodeHandler {
|
func NewNodeHandler(service services.GraphService) *NodeHandler {
|
||||||
return &NodeHandler{
|
return &NodeHandler{
|
||||||
dataService: dataService,
|
service: service,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ func (h *NodeHandler) GetNodeByID(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
node, found := h.dataService.GetNodeByID(nodeID)
|
node, found := h.service.GetNodeByID(nodeID)
|
||||||
if !found {
|
if !found {
|
||||||
c.JSON(http.StatusNotFound, gin.H{
|
c.JSON(http.StatusNotFound, gin.H{
|
||||||
"error": "Not Found",
|
"error": "Not Found",
|
||||||
@@ -49,7 +49,7 @@ func (h *NodeHandler) GetNeighbors(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
neighbors, found := h.dataService.GetNeighbors(nodeID)
|
neighbors, found := h.service.GetNeighbors(nodeID)
|
||||||
if !found {
|
if !found {
|
||||||
c.JSON(http.StatusNotFound, gin.H{
|
c.JSON(http.StatusNotFound, gin.H{
|
||||||
"error": "Not Found",
|
"error": "Not Found",
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SearchHandler struct {
|
type SearchHandler struct {
|
||||||
dataService *services.DataService
|
service services.GraphService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSearchHandler(dataService *services.DataService) *SearchHandler {
|
func NewSearchHandler(service services.GraphService) *SearchHandler {
|
||||||
return &SearchHandler{
|
return &SearchHandler{
|
||||||
dataService: dataService,
|
service: service,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ func (h *SearchHandler) SearchNodes(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
results := h.dataService.SearchNodes(query)
|
results := h.service.SearchNodes(query)
|
||||||
|
|
||||||
c.JSON(http.StatusOK, results)
|
c.JSON(http.StatusOK, results)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-7
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"knowledge-graph-backend/config"
|
"knowledge-graph-backend/config"
|
||||||
"knowledge-graph-backend/handlers"
|
"knowledge-graph-backend/handlers"
|
||||||
|
neo4jClient "knowledge-graph-backend/neo4j"
|
||||||
"knowledge-graph-backend/services"
|
"knowledge-graph-backend/services"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,14 +20,14 @@ func main() {
|
|||||||
log.Fatalf("Failed to load config: %v", err)
|
log.Fatalf("Failed to load config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dataService, err := services.NewDataService(cfg.Data.FilePath)
|
driver := neo4jClient.NewDriver(cfg.Neo4j)
|
||||||
if err != nil {
|
defer driver.Close(context.Background())
|
||||||
log.Fatalf("Failed to initialize data service: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
graphHandler := handlers.NewGraphHandler(dataService)
|
graphService := services.NewNeo4jService(driver)
|
||||||
searchHandler := handlers.NewSearchHandler(dataService)
|
|
||||||
nodeHandler := handlers.NewNodeHandler(dataService)
|
graphHandler := handlers.NewGraphHandler(graphService)
|
||||||
|
searchHandler := handlers.NewSearchHandler(graphService)
|
||||||
|
nodeHandler := handlers.NewNodeHandler(graphService)
|
||||||
|
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,301 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||||
|
|
||||||
|
"knowledge-graph-backend/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Neo4jService struct {
|
||||||
|
driver neo4j.DriverWithContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNeo4jService(driver neo4j.DriverWithContext) *Neo4jService {
|
||||||
|
return &Neo4jService{driver: driver}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Neo4jService) GetGraphData() models.GraphData {
|
||||||
|
ctx := context.Background()
|
||||||
|
var nodes []models.Node
|
||||||
|
var edges []models.Edge
|
||||||
|
|
||||||
|
nodeResult, err := neo4j.ExecuteQuery(ctx, s.driver,
|
||||||
|
"MATCH (n) RETURN n",
|
||||||
|
map[string]any{},
|
||||||
|
neo4j.EagerResultTransformer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error querying nodes: %v\n", err)
|
||||||
|
return models.GraphData{}
|
||||||
|
}
|
||||||
|
for _, record := range nodeResult.Records {
|
||||||
|
if v, ok := record.Get("n"); ok {
|
||||||
|
if n, ok := v.(neo4j.Node); ok {
|
||||||
|
nodes = append(nodes, neo4jNodeToModel(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeResult, err := neo4j.ExecuteQuery(ctx, s.driver,
|
||||||
|
"MATCH (a)-[r]->(b) RETURN a.id AS source, b.id AS target, r",
|
||||||
|
map[string]any{},
|
||||||
|
neo4j.EagerResultTransformer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error querying edges: %v\n", err)
|
||||||
|
return models.GraphData{Nodes: nodes}
|
||||||
|
}
|
||||||
|
for _, record := range edgeResult.Records {
|
||||||
|
source, _ := record.Get("source")
|
||||||
|
target, _ := record.Get("target")
|
||||||
|
v, _ := record.Get("r")
|
||||||
|
if rel, ok := v.(neo4j.Relationship); ok {
|
||||||
|
edge := neo4jRelToModel(rel)
|
||||||
|
if s, ok := source.(string); ok {
|
||||||
|
edge.Source = s
|
||||||
|
}
|
||||||
|
if t, ok := target.(string); ok {
|
||||||
|
edge.Target = t
|
||||||
|
}
|
||||||
|
edges = append(edges, edge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.GraphData{Nodes: nodes, Edges: edges}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Neo4jService) GetNodeByID(id string) (models.Node, bool) {
|
||||||
|
ctx := context.Background()
|
||||||
|
result, err := neo4j.ExecuteQuery(ctx, s.driver,
|
||||||
|
"MATCH (n {id: $id}) RETURN n",
|
||||||
|
map[string]any{"id": id},
|
||||||
|
neo4j.EagerResultTransformer,
|
||||||
|
)
|
||||||
|
if err != nil || len(result.Records) == 0 {
|
||||||
|
return models.Node{}, false
|
||||||
|
}
|
||||||
|
v, _ := result.Records[0].Get("n")
|
||||||
|
if n, ok := v.(neo4j.Node); ok {
|
||||||
|
return neo4jNodeToModel(n), true
|
||||||
|
}
|
||||||
|
return models.Node{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Neo4jService) SearchNodes(query string) []models.Node {
|
||||||
|
if query == "" {
|
||||||
|
return []models.Node{}
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
result, err := neo4j.ExecuteQuery(ctx, s.driver,
|
||||||
|
`MATCH (n)
|
||||||
|
WHERE toLower(n.label) CONTAINS toLower($query)
|
||||||
|
OR toLower(n.id) CONTAINS toLower($query)
|
||||||
|
OR (n.type IS NOT NULL AND toLower(n.type) CONTAINS toLower($query))
|
||||||
|
RETURN n`,
|
||||||
|
map[string]any{"query": query},
|
||||||
|
neo4j.EagerResultTransformer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return []models.Node{}
|
||||||
|
}
|
||||||
|
var nodes []models.Node
|
||||||
|
for _, record := range result.Records {
|
||||||
|
v, _ := record.Get("n")
|
||||||
|
if n, ok := v.(neo4j.Node); ok {
|
||||||
|
nodes = append(nodes, neo4jNodeToModel(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Neo4jService) GetNeighbors(nodeID string) (models.NeighborResponse, bool) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
_, exists := s.GetNodeByID(nodeID)
|
||||||
|
if !exists {
|
||||||
|
return models.NeighborResponse{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := neo4j.ExecuteQuery(ctx, s.driver,
|
||||||
|
`MATCH ({id: $id})-[r]-(m)
|
||||||
|
RETURN m, r, startNode(r).id AS source, endNode(r).id AS target`,
|
||||||
|
map[string]any{"id": nodeID},
|
||||||
|
neo4j.EagerResultTransformer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return models.NeighborResponse{Nodes: []models.Node{}, Edges: []models.Edge{}}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
neighborMap := make(map[string]models.Node)
|
||||||
|
edgeMap := make(map[string]models.Edge)
|
||||||
|
|
||||||
|
for _, record := range result.Records {
|
||||||
|
if v, ok := record.Get("m"); ok {
|
||||||
|
if n, ok := v.(neo4j.Node); ok {
|
||||||
|
modelNode := neo4jNodeToModel(n)
|
||||||
|
neighborMap[modelNode.ID] = modelNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := record.Get("r"); ok {
|
||||||
|
if rel, ok := v.(neo4j.Relationship); ok {
|
||||||
|
edge := neo4jRelToModel(rel)
|
||||||
|
if source, ok := record.Get("source"); ok {
|
||||||
|
if s, ok := source.(string); ok {
|
||||||
|
edge.Source = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if target, ok := record.Get("target"); ok {
|
||||||
|
if t, ok := target.(string); ok {
|
||||||
|
edge.Target = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
edgeMap[edge.ID] = edge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var neighborNodes []models.Node
|
||||||
|
for _, n := range neighborMap {
|
||||||
|
neighborNodes = append(neighborNodes, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
var relatedEdges []models.Edge
|
||||||
|
for _, e := range edgeMap {
|
||||||
|
relatedEdges = append(relatedEdges, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.NeighborResponse{
|
||||||
|
Nodes: neighborNodes,
|
||||||
|
Edges: relatedEdges,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Neo4jService) GetStats() map[string]int {
|
||||||
|
ctx := context.Background()
|
||||||
|
result, err := neo4j.ExecuteQuery(ctx, s.driver,
|
||||||
|
`MATCH (n)
|
||||||
|
RETURN count(n) AS totalNodes,
|
||||||
|
sum(CASE WHEN n.type = '概念' THEN 1 ELSE 0 END) AS conceptNodes,
|
||||||
|
sum(CASE WHEN n.type = '工具' THEN 1 ELSE 0 END) AS toolNodes,
|
||||||
|
sum(CASE WHEN n.type = '应用' THEN 1 ELSE 0 END) AS applicationNodes`,
|
||||||
|
map[string]any{},
|
||||||
|
neo4j.EagerResultTransformer,
|
||||||
|
)
|
||||||
|
if err != nil || len(result.Records) == 0 {
|
||||||
|
return map[string]int{}
|
||||||
|
}
|
||||||
|
|
||||||
|
record := result.Records[0]
|
||||||
|
totalNodes := getInt(record, "totalNodes")
|
||||||
|
conceptNodes := getInt(record, "conceptNodes")
|
||||||
|
toolNodes := getInt(record, "toolNodes")
|
||||||
|
applicationNodes := getInt(record, "applicationNodes")
|
||||||
|
|
||||||
|
totalEdges := 0
|
||||||
|
edgeResult, err := neo4j.ExecuteQuery(ctx, s.driver,
|
||||||
|
"MATCH ()-[r]->() RETURN count(r) AS totalEdges",
|
||||||
|
map[string]any{},
|
||||||
|
neo4j.EagerResultTransformer,
|
||||||
|
)
|
||||||
|
if err == nil && len(edgeResult.Records) > 0 {
|
||||||
|
totalEdges = getInt(edgeResult.Records[0], "totalEdges")
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]int{
|
||||||
|
"totalNodes": totalNodes,
|
||||||
|
"totalEdges": totalEdges,
|
||||||
|
"conceptNodes": conceptNodes,
|
||||||
|
"toolNodes": toolNodes,
|
||||||
|
"applicationNodes": applicationNodes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func neo4jNodeToModel(n neo4j.Node) models.Node {
|
||||||
|
props := n.Props
|
||||||
|
node := models.Node{
|
||||||
|
ID: getStr(props, "id"),
|
||||||
|
Label: getStr(props, "label"),
|
||||||
|
Type: getStr(props, "type"),
|
||||||
|
Properties: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
if v, ok := props["x"]; ok && v != nil {
|
||||||
|
node.X = getFloat64(v)
|
||||||
|
}
|
||||||
|
if v, ok := props["y"]; ok && v != nil {
|
||||||
|
node.Y = getFloat64(v)
|
||||||
|
}
|
||||||
|
if v, ok := props["style"]; ok && v != nil {
|
||||||
|
if s, ok := v.(map[string]interface{}); ok {
|
||||||
|
node.Style = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range props {
|
||||||
|
switch k {
|
||||||
|
case "id", "label", "type", "x", "y", "style":
|
||||||
|
default:
|
||||||
|
node.Properties[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func neo4jRelToModel(r neo4j.Relationship) models.Edge {
|
||||||
|
props := r.Props
|
||||||
|
edge := models.Edge{
|
||||||
|
ID: getStr(props, "id"),
|
||||||
|
Label: getStr(props, "label"),
|
||||||
|
Type: r.Type,
|
||||||
|
Properties: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
if v, ok := props["style"]; ok && v != nil {
|
||||||
|
if s, ok := v.(map[string]interface{}); ok {
|
||||||
|
edge.Style = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range props {
|
||||||
|
switch k {
|
||||||
|
case "id", "label", "style":
|
||||||
|
default:
|
||||||
|
edge.Properties[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return edge
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStr(props map[string]any, key string) string {
|
||||||
|
if v, ok := props[key]; ok && v != nil {
|
||||||
|
if s, ok := v.(string); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFloat64(v any) float64 {
|
||||||
|
switch val := v.(type) {
|
||||||
|
case float64:
|
||||||
|
return val
|
||||||
|
case int64:
|
||||||
|
return float64(val)
|
||||||
|
case int:
|
||||||
|
return float64(val)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInt(record *neo4j.Record, key string) int {
|
||||||
|
v, _ := record.Get(key)
|
||||||
|
switch val := v.(type) {
|
||||||
|
case int64:
|
||||||
|
return int(val)
|
||||||
|
case int:
|
||||||
|
return val
|
||||||
|
case float64:
|
||||||
|
return int(val)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import "knowledge-graph-backend/models"
|
||||||
|
|
||||||
|
type GraphService interface {
|
||||||
|
GetGraphData() models.GraphData
|
||||||
|
GetNodeByID(id string) (models.Node, bool)
|
||||||
|
SearchNodes(query string) []models.Node
|
||||||
|
GetNeighbors(nodeID string) (models.NeighborResponse, bool)
|
||||||
|
GetStats() map[string]int
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user