feat: 增加对neo4j的增改查
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(curl -X POST \"http://localhost:3001/api/nodes\" -H \"Content-Type: application/json\" -d \"{\\\\\"id\\\\\":\\\\\"test_node_1\\\\\",\\\\\"label\\\\\":\\\\\"测试节点\\\\\",\\\\\"type\\\\\":\\\\\"测试类型\\\\\",\\\\\"x\\\\\":100,\\\\\"y\\\\\":200}\" -s)",
|
||||||
|
"Bash(curl -X PUT \"http://localhost:3001/api/nodes/test_node_1\" -H \"Content-Type: application/json\" -d \"{\\\\\"label\\\\\":\\\\\"更新后的测试节点\\\\\",\\\\\"x\\\\\":150}\" -s)",
|
||||||
|
"Bash(curl:*)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is a knowledge graph visualization system with a Go backend providing REST APIs and a React frontend using AntV G6 for interactive graph rendering. The system stores graph data in Neo4j and provides nodes, edges, and relationship visualization.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Backend (`/backend`)
|
||||||
|
- **Entry point**: `main.go` - Sets up Gin router, initializes Neo4j connection, registers handlers
|
||||||
|
- **Handlers** (`/handlers`): HTTP request handlers for `/api/graph`, `/api/search`, `/api/nodes/:id`, `/api/nodes/:id/neighbors`
|
||||||
|
- **Services** (`/services`): Business logic layer. `Neo4jService` queries Neo4j and converts results to domain models
|
||||||
|
- **Models** (`/models`): Domain models (`Node`, `Edge`, `GraphData`) and API response types
|
||||||
|
- **Neo4j client** (`/neo4j`): Connection wrapper using `github.com/neo4j/neo4j-go-driver/v5`
|
||||||
|
- **Config** (`/config`): Configuration loading from `.env` with CORS and server settings
|
||||||
|
|
||||||
|
**Important**: Neo4j connection config is in `backend/config/.env` which contains credentials. The service queries Neo4j by executing Cypher queries and converting results to JSON.
|
||||||
|
|
||||||
|
### Frontend (`/frontend`)
|
||||||
|
- **React 19 + TypeScript + Vite** build system
|
||||||
|
- **State management**: Zustand stores (`graphStore.ts`, `layoutStore.ts`)
|
||||||
|
- **Graph visualization**: AntV G6 v5 for force-directed layout and interactive graphs
|
||||||
|
- **Styling**: Tailwind CSS v4 + HeroUI components
|
||||||
|
- **API client**: Axios (`services/graphApi.ts`)
|
||||||
|
|
||||||
|
**Key frontend components**:
|
||||||
|
- `KnowledgeGraph.tsx` - Main page that wires up search, graph view, and node panel
|
||||||
|
- `GraphView/index.tsx` - G6 graph rendering with node selection, highlighting
|
||||||
|
- `NodePanel/index.tsx` - Sidebar showing selected node details
|
||||||
|
- `SearchBar/index.tsx` - Search input with node autocomplete
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
go run main.go # Start dev server on :3001
|
||||||
|
swag init -g main.go # Regenerate Swagger docs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm run dev # Start dev server on :5173
|
||||||
|
npm run build # Production build
|
||||||
|
npm run lint # Lint code
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Endpoints
|
||||||
|
- `GET /health` - Health check
|
||||||
|
- `GET /api/graph` - Get all graph data (nodes + edges)
|
||||||
|
- `GET /api/graph/stats` - Graph statistics
|
||||||
|
- `GET /api/search?q=query` - Search nodes by label, ID, or type
|
||||||
|
- `GET /api/nodes/:id` - Get node by ID
|
||||||
|
- `GET /api/nodes/:id/neighbors` - Get node neighbors and related edges
|
||||||
|
- `GET /swagger/*` - Swagger UI documentation
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
1. Frontend fetches graph data via `/api/graph`
|
||||||
|
2. Graph data is stored in `graphStore` (Zustand)
|
||||||
|
3. `GraphView` renders with G6 force-directed layout
|
||||||
|
4. Node clicks highlight connected nodes and trigger `NodePanel` updates
|
||||||
|
5. Search queries `/api/search` and selects matching nodes
|
||||||
|
|
||||||
|
## Neo4j Queries
|
||||||
|
|
||||||
|
The backend uses these Cypher query patterns:
|
||||||
|
- All nodes: `MATCH (n) RETURN n`
|
||||||
|
- All edges: `MATCH (a)-[r]->(b) RETURN a.id AS source, b.id AS target, r`
|
||||||
|
- Node by ID: `MATCH (n {id: $id}) RETURN n`
|
||||||
|
- Search: `MATCH (n) WHERE toLower(n.label) CONTAINS toLower($query) OR ... RETURN n`
|
||||||
|
- Neighbors: `MATCH ({id: $id})-[r]-(m) RETURN m, r, startNode(r).id AS source, endNode(r).id AS target`
|
||||||
|
|
||||||
|
## File Conventions
|
||||||
|
|
||||||
|
- Backend handlers follow `*_handler.go` naming
|
||||||
|
- Swagger annotations in handlers with `@Summary`, `@Router`, etc.
|
||||||
|
- Frontend types in `types/graph.ts` shared between components
|
||||||
|
- Graph styling utilities in `utils/graphStyle.ts`
|
||||||
@@ -15,6 +15,99 @@ const docTemplate = `{
|
|||||||
"host": "{{.Host}}",
|
"host": "{{.Host}}",
|
||||||
"basePath": "{{.BasePath}}",
|
"basePath": "{{.BasePath}}",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"/api/edges": {
|
||||||
|
"post": {
|
||||||
|
"description": "在知识图谱中创建一个新边(关系)",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "创建边",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "边创建请求",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.CreateEdgeRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "创建成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.EdgeResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "源节点或目标节点未找到",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "边已存在",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/edges/{id}": {
|
||||||
|
"delete": {
|
||||||
|
"description": "从知识图谱中删除指定的边",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "删除边",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "边ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "删除成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.DeleteResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "边未找到",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/graph": {
|
"/api/graph": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "获取知识图谱的全部节点和边数据",
|
"description": "获取知识图谱的全部节点和边数据",
|
||||||
@@ -62,6 +155,55 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/nodes": {
|
||||||
|
"post": {
|
||||||
|
"description": "在知识图谱中创建一个新节点",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "创建节点",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "节点创建请求",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.CreateNodeRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "创建成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "节点已存在",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/nodes/{id}": {
|
"/api/nodes/{id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "根据节点 ID 获取知识图谱中的节点详细信息",
|
"description": "根据节点 ID 获取知识图谱中的节点详细信息",
|
||||||
@@ -104,6 +246,96 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"description": "更新知识图谱中指定节点的信息",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "更新节点",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "节点ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "节点更新请求",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.UpdateNodeRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "更新成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "节点未找到",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"description": "从知识图谱中删除指定节点及其相关的所有边",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "删除节点",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "节点ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "删除成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.DeleteResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "节点未找到",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/nodes/{id}/neighbors": {
|
"/api/nodes/{id}/neighbors": {
|
||||||
@@ -190,6 +422,107 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
"models.CreateEdgeRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"label",
|
||||||
|
"source",
|
||||||
|
"target"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "边的唯一标识",
|
||||||
|
"type": "string",
|
||||||
|
"example": "edge_123"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"description": "边的显示标签",
|
||||||
|
"type": "string",
|
||||||
|
"example": "包含"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "自定义属性",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"description": "源节点ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_1"
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"description": "目标节点ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_2"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "边的类型(可选,默认为关系类型)",
|
||||||
|
"type": "string",
|
||||||
|
"example": "CONTAINS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.CreateNodeRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"label",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "节点唯一标识",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_123"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"description": "节点显示标签",
|
||||||
|
"type": "string",
|
||||||
|
"example": "人工智能"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "自定义属性",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "节点类型",
|
||||||
|
"type": "string",
|
||||||
|
"example": "概念"
|
||||||
|
},
|
||||||
|
"x": {
|
||||||
|
"description": "X坐标(可选)",
|
||||||
|
"type": "number",
|
||||||
|
"example": 100
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"description": "Y坐标(可选)",
|
||||||
|
"type": "number",
|
||||||
|
"example": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.DeleteResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "被删除的实体ID(可选)",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_123"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"description": "消息",
|
||||||
|
"type": "string",
|
||||||
|
"example": "节点已成功删除"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"description": "是否成功",
|
||||||
|
"type": "boolean",
|
||||||
|
"example": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.Edge": {
|
"models.Edge": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -218,6 +551,41 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.EdgeResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "边的ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "edge_123"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"description": "边的显示标签",
|
||||||
|
"type": "string",
|
||||||
|
"example": "包含"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "自定义属性",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"description": "源节点ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_1"
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"description": "目标节点ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_2"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "边的类型",
|
||||||
|
"type": "string",
|
||||||
|
"example": "CONTAINS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.ErrorResponse": {
|
"models.ErrorResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -325,6 +693,36 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"models.UpdateNodeRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": {
|
||||||
|
"description": "节点显示标签",
|
||||||
|
"type": "string",
|
||||||
|
"example": "机器学习"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "自定义属性(更新)",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "节点类型",
|
||||||
|
"type": "string",
|
||||||
|
"example": "工具"
|
||||||
|
},
|
||||||
|
"x": {
|
||||||
|
"description": "X坐标",
|
||||||
|
"type": "number",
|
||||||
|
"example": 150
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"description": "Y坐标",
|
||||||
|
"type": "number",
|
||||||
|
"example": 250
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|||||||
+413
-5
@@ -9,10 +9,105 @@
|
|||||||
"host": "localhost:3001",
|
"host": "localhost:3001",
|
||||||
"basePath": "/",
|
"basePath": "/",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"/api/edges": {
|
||||||
|
"post": {
|
||||||
|
"description": "在知识图谱中创建一个新边(关系)",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "创建边",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "边创建请求",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.CreateEdgeRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "创建成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.EdgeResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "源节点或目标节点未找到",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "边已存在",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/edges/{id}": {
|
||||||
|
"delete": {
|
||||||
|
"description": "从知识图谱中删除指定的边",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "删除边",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "边ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "删除成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.DeleteResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "边未找到",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/graph": {
|
"/api/graph": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "获取知识图谱的全部节点和边数据",
|
"description": "获取知识图谱的全部节点和边数据",
|
||||||
"produces": ["application/json"],
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
"summary": "获取图数据",
|
"summary": "获取图数据",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
@@ -33,7 +128,9 @@
|
|||||||
"/api/graph/stats": {
|
"/api/graph/stats": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "获取知识图谱的节点数、边数等统计信息",
|
"description": "获取知识图谱的节点数、边数等统计信息",
|
||||||
"produces": ["application/json"],
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
"summary": "获取图统计信息",
|
"summary": "获取图统计信息",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
@@ -52,10 +149,61 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/nodes": {
|
||||||
|
"post": {
|
||||||
|
"description": "在知识图谱中创建一个新节点",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "创建节点",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "节点创建请求",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.CreateNodeRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "创建成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"409": {
|
||||||
|
"description": "节点已存在",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/nodes/{id}": {
|
"/api/nodes/{id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "根据节点 ID 获取知识图谱中的节点详细信息",
|
"description": "根据节点 ID 获取知识图谱中的节点详细信息",
|
||||||
"produces": ["application/json"],
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
"summary": "获取节点详情",
|
"summary": "获取节点详情",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -92,12 +240,104 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"description": "更新知识图谱中指定节点的信息",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "更新节点",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "节点ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "节点更新请求",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.UpdateNodeRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "更新成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.Node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "请求错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "节点未找到",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"description": "从知识图谱中删除指定节点及其相关的所有边",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "删除节点",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "节点ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "删除成功",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.DeleteResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "节点未找到",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "内部错误",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/nodes/{id}/neighbors": {
|
"/api/nodes/{id}/neighbors": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "根据节点 ID 获取其所有邻居节点及关联边",
|
"description": "根据节点 ID 获取其所有邻居节点及关联边",
|
||||||
"produces": ["application/json"],
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
"summary": "获取节点邻居",
|
"summary": "获取节点邻居",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -139,7 +379,9 @@
|
|||||||
"/api/search": {
|
"/api/search": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "根据关键词搜索知识图谱中的节点",
|
"description": "根据关键词搜索知识图谱中的节点",
|
||||||
"produces": ["application/json"],
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
"summary": "搜索节点",
|
"summary": "搜索节点",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
@@ -174,6 +416,107 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
"models.CreateEdgeRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"label",
|
||||||
|
"source",
|
||||||
|
"target"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "边的唯一标识",
|
||||||
|
"type": "string",
|
||||||
|
"example": "edge_123"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"description": "边的显示标签",
|
||||||
|
"type": "string",
|
||||||
|
"example": "包含"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "自定义属性",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"description": "源节点ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_1"
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"description": "目标节点ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_2"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "边的类型(可选,默认为关系类型)",
|
||||||
|
"type": "string",
|
||||||
|
"example": "CONTAINS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.CreateNodeRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"label",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "节点唯一标识",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_123"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"description": "节点显示标签",
|
||||||
|
"type": "string",
|
||||||
|
"example": "人工智能"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "自定义属性",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "节点类型",
|
||||||
|
"type": "string",
|
||||||
|
"example": "概念"
|
||||||
|
},
|
||||||
|
"x": {
|
||||||
|
"description": "X坐标(可选)",
|
||||||
|
"type": "number",
|
||||||
|
"example": 100
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"description": "Y坐标(可选)",
|
||||||
|
"type": "number",
|
||||||
|
"example": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models.DeleteResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "被删除的实体ID(可选)",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_123"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"description": "消息",
|
||||||
|
"type": "string",
|
||||||
|
"example": "节点已成功删除"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"description": "是否成功",
|
||||||
|
"type": "boolean",
|
||||||
|
"example": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.Edge": {
|
"models.Edge": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -202,6 +545,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.EdgeResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "边的ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "edge_123"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"description": "边的显示标签",
|
||||||
|
"type": "string",
|
||||||
|
"example": "包含"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "自定义属性",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"description": "源节点ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_1"
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"description": "目标节点ID",
|
||||||
|
"type": "string",
|
||||||
|
"example": "node_2"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "边的类型",
|
||||||
|
"type": "string",
|
||||||
|
"example": "CONTAINS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.ErrorResponse": {
|
"models.ErrorResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -309,6 +687,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"models.UpdateNodeRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": {
|
||||||
|
"description": "节点显示标签",
|
||||||
|
"type": "string",
|
||||||
|
"example": "机器学习"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "自定义属性(更新)",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "节点类型",
|
||||||
|
"type": "string",
|
||||||
|
"example": "工具"
|
||||||
|
},
|
||||||
|
"x": {
|
||||||
|
"description": "X坐标",
|
||||||
|
"type": "number",
|
||||||
|
"example": 150
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"description": "Y坐标",
|
||||||
|
"type": "number",
|
||||||
|
"example": 250
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+300
-19
@@ -1,5 +1,83 @@
|
|||||||
basePath: /
|
basePath: /
|
||||||
definitions:
|
definitions:
|
||||||
|
models.CreateEdgeRequest:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: 边的唯一标识
|
||||||
|
example: edge_123
|
||||||
|
type: string
|
||||||
|
label:
|
||||||
|
description: 边的显示标签
|
||||||
|
example: 包含
|
||||||
|
type: string
|
||||||
|
properties:
|
||||||
|
additionalProperties: true
|
||||||
|
description: 自定义属性
|
||||||
|
type: object
|
||||||
|
source:
|
||||||
|
description: 源节点ID
|
||||||
|
example: node_1
|
||||||
|
type: string
|
||||||
|
target:
|
||||||
|
description: 目标节点ID
|
||||||
|
example: node_2
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: 边的类型(可选,默认为关系类型)
|
||||||
|
example: CONTAINS
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- label
|
||||||
|
- source
|
||||||
|
- target
|
||||||
|
type: object
|
||||||
|
models.CreateNodeRequest:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: 节点唯一标识
|
||||||
|
example: node_123
|
||||||
|
type: string
|
||||||
|
label:
|
||||||
|
description: 节点显示标签
|
||||||
|
example: 人工智能
|
||||||
|
type: string
|
||||||
|
properties:
|
||||||
|
additionalProperties: true
|
||||||
|
description: 自定义属性
|
||||||
|
type: object
|
||||||
|
type:
|
||||||
|
description: 节点类型
|
||||||
|
example: 概念
|
||||||
|
type: string
|
||||||
|
x:
|
||||||
|
description: X坐标(可选)
|
||||||
|
example: 100
|
||||||
|
type: number
|
||||||
|
"y":
|
||||||
|
description: Y坐标(可选)
|
||||||
|
example: 200
|
||||||
|
type: number
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- label
|
||||||
|
- type
|
||||||
|
type: object
|
||||||
|
models.DeleteResponse:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: 被删除的实体ID(可选)
|
||||||
|
example: node_123
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
description: 消息
|
||||||
|
example: 节点已成功删除
|
||||||
|
type: string
|
||||||
|
success:
|
||||||
|
description: 是否成功
|
||||||
|
example: true
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
models.Edge:
|
models.Edge:
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
@@ -19,6 +97,33 @@ definitions:
|
|||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
models.EdgeResponse:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: 边的ID
|
||||||
|
example: edge_123
|
||||||
|
type: string
|
||||||
|
label:
|
||||||
|
description: 边的显示标签
|
||||||
|
example: 包含
|
||||||
|
type: string
|
||||||
|
properties:
|
||||||
|
additionalProperties: true
|
||||||
|
description: 自定义属性
|
||||||
|
type: object
|
||||||
|
source:
|
||||||
|
description: 源节点ID
|
||||||
|
example: node_1
|
||||||
|
type: string
|
||||||
|
target:
|
||||||
|
description: 目标节点ID
|
||||||
|
example: node_2
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
description: 边的类型
|
||||||
|
example: CONTAINS
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
models.ErrorResponse:
|
models.ErrorResponse:
|
||||||
properties:
|
properties:
|
||||||
error:
|
error:
|
||||||
@@ -30,22 +135,22 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
edges:
|
edges:
|
||||||
items:
|
items:
|
||||||
$ref: "#/definitions/models.Edge"
|
$ref: '#/definitions/models.Edge'
|
||||||
type: array
|
type: array
|
||||||
nodes:
|
nodes:
|
||||||
items:
|
items:
|
||||||
$ref: "#/definitions/models.Node"
|
$ref: '#/definitions/models.Node'
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
models.NeighborResponse:
|
models.NeighborResponse:
|
||||||
properties:
|
properties:
|
||||||
edges:
|
edges:
|
||||||
items:
|
items:
|
||||||
$ref: "#/definitions/models.Edge"
|
$ref: '#/definitions/models.Edge'
|
||||||
type: array
|
type: array
|
||||||
nodes:
|
nodes:
|
||||||
items:
|
items:
|
||||||
$ref: "#/definitions/models.Node"
|
$ref: '#/definitions/models.Node'
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
models.Node:
|
models.Node:
|
||||||
@@ -88,9 +193,32 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
results:
|
results:
|
||||||
items:
|
items:
|
||||||
$ref: "#/definitions/models.Node"
|
$ref: '#/definitions/models.Node'
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
|
models.UpdateNodeRequest:
|
||||||
|
properties:
|
||||||
|
label:
|
||||||
|
description: 节点显示标签
|
||||||
|
example: 机器学习
|
||||||
|
type: string
|
||||||
|
properties:
|
||||||
|
additionalProperties: true
|
||||||
|
description: 自定义属性(更新)
|
||||||
|
type: object
|
||||||
|
type:
|
||||||
|
description: 节点类型
|
||||||
|
example: 工具
|
||||||
|
type: string
|
||||||
|
x:
|
||||||
|
description: X坐标
|
||||||
|
example: 150
|
||||||
|
type: number
|
||||||
|
"y":
|
||||||
|
description: Y坐标
|
||||||
|
example: 250
|
||||||
|
type: number
|
||||||
|
type: object
|
||||||
host: localhost:3001
|
host: localhost:3001
|
||||||
info:
|
info:
|
||||||
contact: {}
|
contact: {}
|
||||||
@@ -98,6 +226,67 @@ info:
|
|||||||
title: 知识图谱 API
|
title: 知识图谱 API
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
paths:
|
paths:
|
||||||
|
/api/edges:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 在知识图谱中创建一个新边(关系)
|
||||||
|
parameters:
|
||||||
|
- description: 边创建请求
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.CreateEdgeRequest'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 创建成功
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.EdgeResponse'
|
||||||
|
"400":
|
||||||
|
description: 请求错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"404":
|
||||||
|
description: 源节点或目标节点未找到
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"409":
|
||||||
|
description: 边已存在
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
summary: 创建边
|
||||||
|
/api/edges/{id}:
|
||||||
|
delete:
|
||||||
|
description: 从知识图谱中删除指定的边
|
||||||
|
parameters:
|
||||||
|
- description: 边ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 删除成功
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.DeleteResponse'
|
||||||
|
"404":
|
||||||
|
description: 边未找到
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
summary: 删除边
|
||||||
/api/graph:
|
/api/graph:
|
||||||
get:
|
get:
|
||||||
description: 获取知识图谱的全部节点和边数据
|
description: 获取知识图谱的全部节点和边数据
|
||||||
@@ -107,11 +296,11 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: 成功
|
description: 成功
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.GraphData"
|
$ref: '#/definitions/models.GraphData'
|
||||||
"500":
|
"500":
|
||||||
description: 内部错误
|
description: 内部错误
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
summary: 获取图数据
|
summary: 获取图数据
|
||||||
/api/graph/stats:
|
/api/graph/stats:
|
||||||
get:
|
get:
|
||||||
@@ -127,9 +316,65 @@ paths:
|
|||||||
"500":
|
"500":
|
||||||
description: 内部错误
|
description: 内部错误
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
summary: 获取图统计信息
|
summary: 获取图统计信息
|
||||||
|
/api/nodes:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 在知识图谱中创建一个新节点
|
||||||
|
parameters:
|
||||||
|
- description: 节点创建请求
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.CreateNodeRequest'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 创建成功
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Node'
|
||||||
|
"400":
|
||||||
|
description: 请求错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"409":
|
||||||
|
description: 节点已存在
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
summary: 创建节点
|
||||||
/api/nodes/{id}:
|
/api/nodes/{id}:
|
||||||
|
delete:
|
||||||
|
description: 从知识图谱中删除指定节点及其相关的所有边
|
||||||
|
parameters:
|
||||||
|
- description: 节点ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 删除成功
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.DeleteResponse'
|
||||||
|
"404":
|
||||||
|
description: 节点未找到
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
summary: 删除节点
|
||||||
get:
|
get:
|
||||||
description: 根据节点 ID 获取知识图谱中的节点详细信息
|
description: 根据节点 ID 获取知识图谱中的节点详细信息
|
||||||
parameters:
|
parameters:
|
||||||
@@ -144,20 +389,56 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: 成功
|
description: 成功
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.NodeResponse"
|
$ref: '#/definitions/models.NodeResponse'
|
||||||
"400":
|
"400":
|
||||||
description: 请求错误
|
description: 请求错误
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
"404":
|
"404":
|
||||||
description: 节点未找到
|
description: 节点未找到
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
"500":
|
"500":
|
||||||
description: 内部错误
|
description: 内部错误
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
summary: 获取节点详情
|
summary: 获取节点详情
|
||||||
|
put:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 更新知识图谱中指定节点的信息
|
||||||
|
parameters:
|
||||||
|
- description: 节点ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- description: 节点更新请求
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.UpdateNodeRequest'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: 更新成功
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.Node'
|
||||||
|
"400":
|
||||||
|
description: 请求错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"404":
|
||||||
|
description: 节点未找到
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
"500":
|
||||||
|
description: 内部错误
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
|
summary: 更新节点
|
||||||
/api/nodes/{id}/neighbors:
|
/api/nodes/{id}/neighbors:
|
||||||
get:
|
get:
|
||||||
description: 根据节点 ID 获取其所有邻居节点及关联边
|
description: 根据节点 ID 获取其所有邻居节点及关联边
|
||||||
@@ -173,19 +454,19 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: 成功
|
description: 成功
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.NeighborResponse"
|
$ref: '#/definitions/models.NeighborResponse'
|
||||||
"400":
|
"400":
|
||||||
description: 请求错误
|
description: 请求错误
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
"404":
|
"404":
|
||||||
description: 节点未找到
|
description: 节点未找到
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
"500":
|
"500":
|
||||||
description: 内部错误
|
description: 内部错误
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
summary: 获取节点邻居
|
summary: 获取节点邻居
|
||||||
/api/search:
|
/api/search:
|
||||||
get:
|
get:
|
||||||
@@ -202,14 +483,14 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: 成功
|
description: 成功
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.SearchResponse"
|
$ref: '#/definitions/models.SearchResponse'
|
||||||
"400":
|
"400":
|
||||||
description: 请求错误
|
description: 请求错误
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
"500":
|
"500":
|
||||||
description: 内部错误
|
description: 内部错误
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/models.ErrorResponse"
|
$ref: '#/definitions/models.ErrorResponse'
|
||||||
summary: 搜索节点
|
summary: 搜索节点
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"knowledge-graph-backend/models"
|
||||||
|
"knowledge-graph-backend/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeCRUDHandler 节点的增删改处理器
|
||||||
|
type NodeCRUDHandler struct {
|
||||||
|
service services.GraphService
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNodeCRUDHandler 创建节点CRUD处理器
|
||||||
|
func NewNodeCRUDHandler(service services.GraphService) *NodeCRUDHandler {
|
||||||
|
return &NodeCRUDHandler{
|
||||||
|
service: service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EdgeCRUDHandler 边的增删改处理器
|
||||||
|
type EdgeCRUDHandler struct {
|
||||||
|
service services.GraphService
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEdgeCRUDHandler 创建边CRUD处理器
|
||||||
|
func NewEdgeCRUDHandler(service services.GraphService) *EdgeCRUDHandler {
|
||||||
|
return &EdgeCRUDHandler{
|
||||||
|
service: service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNode 创建新节点
|
||||||
|
// @Summary 创建节点
|
||||||
|
// @Description 在知识图谱中创建一个新节点
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param request body models.CreateNodeRequest true "节点创建请求"
|
||||||
|
// @Success 200 {object} models.Node "创建成功"
|
||||||
|
// @Failure 400 {object} models.ErrorResponse "请求错误"
|
||||||
|
// @Failure 409 {object} models.ErrorResponse "节点已存在"
|
||||||
|
// @Failure 500 {object} models.ErrorResponse "内部错误"
|
||||||
|
// @Router /api/nodes [post]
|
||||||
|
func (h *NodeCRUDHandler) CreateNode(c *gin.Context) {
|
||||||
|
var req models.CreateNodeRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||||
|
Error: "invalid_request",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node, err := h.service.CreateNode(req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusConflict, models.ErrorResponse{
|
||||||
|
Error: "create_failed",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateNode 更新节点
|
||||||
|
// @Summary 更新节点
|
||||||
|
// @Description 更新知识图谱中指定节点的信息
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "节点ID"
|
||||||
|
// @Param request body models.UpdateNodeRequest true "节点更新请求"
|
||||||
|
// @Success 200 {object} models.Node "更新成功"
|
||||||
|
// @Failure 400 {object} models.ErrorResponse "请求错误"
|
||||||
|
// @Failure 404 {object} models.ErrorResponse "节点未找到"
|
||||||
|
// @Failure 500 {object} models.ErrorResponse "内部错误"
|
||||||
|
// @Router /api/nodes/{id} [put]
|
||||||
|
func (h *NodeCRUDHandler) UpdateNode(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
|
||||||
|
var req models.UpdateNodeRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||||
|
Error: "invalid_request",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node, err := h.service.UpdateNode(id, req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||||
|
Error: "update_failed",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteNode 删除节点
|
||||||
|
// @Summary 删除节点
|
||||||
|
// @Description 从知识图谱中删除指定节点及其相关的所有边
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "节点ID"
|
||||||
|
// @Success 200 {object} models.DeleteResponse "删除成功"
|
||||||
|
// @Failure 404 {object} models.ErrorResponse "节点未找到"
|
||||||
|
// @Failure 500 {object} models.ErrorResponse "内部错误"
|
||||||
|
// @Router /api/nodes/{id} [delete]
|
||||||
|
func (h *NodeCRUDHandler) DeleteNode(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
|
||||||
|
err := h.service.DeleteNode(id)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||||
|
Error: "delete_failed",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, models.DeleteResponse{
|
||||||
|
Success: true,
|
||||||
|
Message: "节点已成功删除",
|
||||||
|
ID: id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEdge 创建新边
|
||||||
|
// @Summary 创建边
|
||||||
|
// @Description 在知识图谱中创建一个新边(关系)
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param request body models.CreateEdgeRequest true "边创建请求"
|
||||||
|
// @Success 200 {object} models.EdgeResponse "创建成功"
|
||||||
|
// @Failure 400 {object} models.ErrorResponse "请求错误"
|
||||||
|
// @Failure 404 {object} models.ErrorResponse "源节点或目标节点未找到"
|
||||||
|
// @Failure 409 {object} models.ErrorResponse "边已存在"
|
||||||
|
// @Failure 500 {object} models.ErrorResponse "内部错误"
|
||||||
|
// @Router /api/edges [post]
|
||||||
|
func (h *EdgeCRUDHandler) CreateEdge(c *gin.Context) {
|
||||||
|
var req models.CreateEdgeRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||||
|
Error: "invalid_request",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
edge, err := h.service.CreateEdge(req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusConflict, models.ErrorResponse{
|
||||||
|
Error: "create_failed",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为响应格式
|
||||||
|
response := models.EdgeResponse{
|
||||||
|
ID: edge.ID,
|
||||||
|
Source: edge.Source,
|
||||||
|
Target: edge.Target,
|
||||||
|
Label: edge.Label,
|
||||||
|
Type: edge.Type,
|
||||||
|
Properties: edge.Properties,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEdge 删除边
|
||||||
|
// @Summary 删除边
|
||||||
|
// @Description 从知识图谱中删除指定的边
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "边ID"
|
||||||
|
// @Success 200 {object} models.DeleteResponse "删除成功"
|
||||||
|
// @Failure 404 {object} models.ErrorResponse "边未找到"
|
||||||
|
// @Failure 500 {object} models.ErrorResponse "内部错误"
|
||||||
|
// @Router /api/edges/{id} [delete]
|
||||||
|
func (h *EdgeCRUDHandler) DeleteEdge(c *gin.Context) {
|
||||||
|
edgeID := c.Param("id")
|
||||||
|
|
||||||
|
err := h.service.DeleteEdge(edgeID)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
||||||
|
Error: "delete_failed",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, models.DeleteResponse{
|
||||||
|
Success: true,
|
||||||
|
Message: "边已成功删除",
|
||||||
|
ID: edgeID,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -36,11 +36,5 @@ func (h *GraphHandler) GetGraphData(c *gin.Context) {
|
|||||||
// @Router /api/graph/stats [get]
|
// @Router /api/graph/stats [get]
|
||||||
func (h *GraphHandler) GetStats(c *gin.Context) {
|
func (h *GraphHandler) GetStats(c *gin.Context) {
|
||||||
stats := h.service.GetStats()
|
stats := h.service.GetStats()
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, stats)
|
||||||
"stats": stats,
|
|
||||||
"timestamp": gin.H{
|
|
||||||
"totalNodes": stats["totalNodes"],
|
|
||||||
"totalEdges": stats["totalEdges"],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func main() {
|
|||||||
searchHandler := handlers.NewSearchHandler(graphService)
|
searchHandler := handlers.NewSearchHandler(graphService)
|
||||||
nodeHandler := handlers.NewNodeHandler(graphService)
|
nodeHandler := handlers.NewNodeHandler(graphService)
|
||||||
|
|
||||||
|
// 增删改处理器
|
||||||
|
nodeCRUDHandler := handlers.NewNodeCRUDHandler(graphService)
|
||||||
|
edgeCRUDHandler := handlers.NewEdgeCRUDHandler(graphService)
|
||||||
|
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
router.Use(cors.New(cfg.CORS.ToGinConfig()))
|
router.Use(cors.New(cfg.CORS.ToGinConfig()))
|
||||||
@@ -64,8 +68,18 @@ func main() {
|
|||||||
|
|
||||||
api.GET("/search", searchHandler.SearchNodes)
|
api.GET("/search", searchHandler.SearchNodes)
|
||||||
|
|
||||||
|
// 节点查询路由
|
||||||
api.GET("/nodes/:id", nodeHandler.GetNodeByID)
|
api.GET("/nodes/:id", nodeHandler.GetNodeByID)
|
||||||
api.GET("/nodes/:id/neighbors", nodeHandler.GetNeighbors)
|
api.GET("/nodes/:id/neighbors", nodeHandler.GetNeighbors)
|
||||||
|
|
||||||
|
// 节点增删改路由
|
||||||
|
api.POST("/nodes", nodeCRUDHandler.CreateNode)
|
||||||
|
api.PUT("/nodes/:id", nodeCRUDHandler.UpdateNode)
|
||||||
|
api.DELETE("/nodes/:id", nodeCRUDHandler.DeleteNode)
|
||||||
|
|
||||||
|
// 边增删改路由
|
||||||
|
api.POST("/edges", edgeCRUDHandler.CreateEdge)
|
||||||
|
api.DELETE("/edges/:id", edgeCRUDHandler.DeleteEdge)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Knowledge Graph API Server running on http://localhost:" + cfg.Server.Port)
|
log.Println("Knowledge Graph API Server running on http://localhost:" + cfg.Server.Port)
|
||||||
@@ -77,6 +91,11 @@ func main() {
|
|||||||
log.Println(" GET /api/search?q=query - Search nodes")
|
log.Println(" GET /api/search?q=query - Search nodes")
|
||||||
log.Println(" GET /api/nodes/:id - Get node by ID")
|
log.Println(" GET /api/nodes/:id - Get node by ID")
|
||||||
log.Println(" GET /api/nodes/:id/neighbors - Get node neighbors")
|
log.Println(" GET /api/nodes/:id/neighbors - Get node neighbors")
|
||||||
|
log.Println(" POST /api/nodes - Create new node")
|
||||||
|
log.Println(" PUT /api/nodes/:id - Update node")
|
||||||
|
log.Println(" DELETE /api/nodes/:id - Delete node")
|
||||||
|
log.Println(" POST /api/edges - Create new edge")
|
||||||
|
log.Println(" DELETE /api/edges/:id - Delete edge")
|
||||||
|
|
||||||
if err := router.Run(":" + cfg.Server.Port); err != nil {
|
if err := router.Run(":" + cfg.Server.Port); err != nil {
|
||||||
log.Fatalf("Failed to start server: %v", err)
|
log.Fatalf("Failed to start server: %v", err)
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
// CreateNodeRequest 创建节点的请求结构
|
||||||
|
type CreateNodeRequest struct {
|
||||||
|
ID string `json:"id" binding:"required" example:"node_123"` // 节点唯一标识
|
||||||
|
Label string `json:"label" binding:"required" example:"人工智能"` // 节点显示标签
|
||||||
|
Type string `json:"type" binding:"required" example:"概念"` // 节点类型
|
||||||
|
X float64 `json:"x" example:"100"` // X坐标(可选)
|
||||||
|
Y float64 `json:"y" example:"200"` // Y坐标(可选)
|
||||||
|
Properties map[string]interface{} `json:"properties,omitempty"` // 自定义属性
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateNodeRequest 更新节点的请求结构
|
||||||
|
type UpdateNodeRequest struct {
|
||||||
|
Label string `json:"label,omitempty" example:"机器学习"` // 节点显示标签
|
||||||
|
Type string `json:"type,omitempty" example:"工具"` // 节点类型
|
||||||
|
X *float64 `json:"x,omitempty" example:"150"` // X坐标
|
||||||
|
Y *float64 `json:"y,omitempty" example:"250"` // Y坐标
|
||||||
|
Properties map[string]interface{} `json:"properties,omitempty"` // 自定义属性(更新)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResponse 删除操作的响应结构
|
||||||
|
type DeleteResponse struct {
|
||||||
|
Success bool `json:"success" example:"true"` // 是否成功
|
||||||
|
Message string `json:"message" example:"节点已成功删除"` // 消息
|
||||||
|
ID string `json:"id,omitempty" example:"node_123"` // 被删除的实体ID(可选)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEdgeRequest 创建边的请求结构
|
||||||
|
type CreateEdgeRequest struct {
|
||||||
|
ID string `json:"id" binding:"required" example:"edge_123"` // 边的唯一标识
|
||||||
|
Source string `json:"source" binding:"required" example:"node_1"` // 源节点ID
|
||||||
|
Target string `json:"target" binding:"required" example:"node_2"` // 目标节点ID
|
||||||
|
Label string `json:"label" binding:"required" example:"包含"` // 边的显示标签
|
||||||
|
Type string `json:"type" example:"CONTAINS"` // 边的类型(可选,默认为关系类型)
|
||||||
|
Properties map[string]interface{} `json:"properties,omitempty"` // 自定义属性
|
||||||
|
}
|
||||||
|
|
||||||
|
// EdgeResponse 边的响应结构
|
||||||
|
type EdgeResponse struct {
|
||||||
|
ID string `json:"id" example:"edge_123"` // 边的ID
|
||||||
|
Source string `json:"source" example:"node_1"` // 源节点ID
|
||||||
|
Target string `json:"target" example:"node_2"` // 目标节点ID
|
||||||
|
Label string `json:"label" example:"包含"` // 边的显示标签
|
||||||
|
Type string `json:"type" example:"CONTAINS"` // 边的类型
|
||||||
|
Properties map[string]interface{} `json:"properties,omitempty"` // 自定义属性
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package services
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||||
|
|
||||||
@@ -299,3 +300,354 @@ func getInt(record *neo4j.Record, key string) int {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateNode 创建新节点
|
||||||
|
func (s *Neo4jService) CreateNode(req models.CreateNodeRequest) (models.Node, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 检查节点是否已存在
|
||||||
|
checkQuery := "MATCH (n {id: $id}) RETURN n"
|
||||||
|
checkResult, err := neo4j.ExecuteQuery(ctx, s.driver, checkQuery,
|
||||||
|
map[string]any{"id": req.ID}, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
return models.Node{}, fmt.Errorf("error checking node existence: %w", err)
|
||||||
|
}
|
||||||
|
if len(checkResult.Records) > 0 {
|
||||||
|
return models.Node{}, fmt.Errorf("node with id %s already exists", req.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建创建属性
|
||||||
|
props := map[string]any{
|
||||||
|
"id": req.ID,
|
||||||
|
"label": req.Label,
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Type != "" {
|
||||||
|
props["type"] = req.Type
|
||||||
|
}
|
||||||
|
if req.X != 0 {
|
||||||
|
props["x"] = req.X
|
||||||
|
}
|
||||||
|
if req.Y != 0 {
|
||||||
|
props["y"] = req.Y
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并自定义属性(避免覆盖系统属性)
|
||||||
|
if req.Properties != nil {
|
||||||
|
for k, v := range req.Properties {
|
||||||
|
if k != "id" && k != "label" && k != "type" && k != "x" && k != "y" {
|
||||||
|
props[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行创建
|
||||||
|
nodeType := "Node"
|
||||||
|
if req.Type != "" {
|
||||||
|
nodeType = req.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
CREATE (n:%s)
|
||||||
|
SET n = $props
|
||||||
|
RETURN n`, nodeType)
|
||||||
|
|
||||||
|
result, err := neo4j.ExecuteQuery(ctx, s.driver, query,
|
||||||
|
map[string]any{"props": props}, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error creating node: %v\n", err)
|
||||||
|
return models.Node{}, fmt.Errorf("failed to create node: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.Records) == 0 {
|
||||||
|
return models.Node{}, fmt.Errorf("failed to create node: no result returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := result.Records[0].Get("n")
|
||||||
|
if n, ok := v.(neo4j.Node); ok {
|
||||||
|
return neo4jNodeToModel(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.Node{}, fmt.Errorf("unexpected result type while creating node")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNodeV2 创建新节点(改进版)
|
||||||
|
func (s *Neo4jService) CreateNodeV2(req models.CreateNodeRequest) (models.Node, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 先检查节点是否已存在
|
||||||
|
checkQuery := "MATCH (n {id: $id}) RETURN n"
|
||||||
|
checkResult, err := neo4j.ExecuteQuery(ctx, s.driver, checkQuery,
|
||||||
|
map[string]any{"id": req.ID}, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
return models.Node{}, err
|
||||||
|
}
|
||||||
|
if len(checkResult.Records) > 0 {
|
||||||
|
return models.Node{}, fmt.Errorf("node with id %s already exists", req.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建创建属性
|
||||||
|
props := map[string]any{
|
||||||
|
"id": req.ID,
|
||||||
|
"label": req.Label,
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Type != "" {
|
||||||
|
props["type"] = req.Type
|
||||||
|
}
|
||||||
|
if req.X != 0 {
|
||||||
|
props["x"] = req.X
|
||||||
|
}
|
||||||
|
if req.Y != 0 {
|
||||||
|
props["y"] = req.Y
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并自定义属性(避免覆盖系统属性)
|
||||||
|
if req.Properties != nil {
|
||||||
|
for k, v := range req.Properties {
|
||||||
|
if k != "id" && k != "label" && k != "type" && k != "x" && k != "y" {
|
||||||
|
props[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行创建
|
||||||
|
nodeType := "Node"
|
||||||
|
if req.Type != "" {
|
||||||
|
nodeType = req.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
CREATE (n:%s)
|
||||||
|
SET n = $props
|
||||||
|
RETURN n`, nodeType)
|
||||||
|
|
||||||
|
result, err := neo4j.ExecuteQuery(ctx, s.driver, query,
|
||||||
|
map[string]any{"props": props}, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error creating node: %v\n", err)
|
||||||
|
return models.Node{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.Records) == 0 {
|
||||||
|
return models.Node{}, fmt.Errorf("failed to create node")
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := result.Records[0].Get("n")
|
||||||
|
if n, ok := v.(neo4j.Node); ok {
|
||||||
|
return neo4jNodeToModel(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.Node{}, fmt.Errorf("unexpected result type while creating node")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateNode 更新节点
|
||||||
|
func (s *Neo4jService) UpdateNode(id string, req models.UpdateNodeRequest) (models.Node, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 检查节点是否存在
|
||||||
|
_, exists := s.GetNodeByID(id)
|
||||||
|
if !exists {
|
||||||
|
return models.Node{}, fmt.Errorf("node with id %s not found", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建更新语句
|
||||||
|
setClauses := []string{}
|
||||||
|
params := map[string]any{"id": id}
|
||||||
|
|
||||||
|
if req.Label != "" {
|
||||||
|
setClauses = append(setClauses, "n.label = $label")
|
||||||
|
params["label"] = req.Label
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Type != "" {
|
||||||
|
setClauses = append(setClauses, "n.type = $type")
|
||||||
|
params["type"] = req.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.X != nil {
|
||||||
|
setClauses = append(setClauses, "n.x = $x")
|
||||||
|
params["x"] = *req.X
|
||||||
|
}
|
||||||
|
if req.Y != nil {
|
||||||
|
setClauses = append(setClauses, "n.y = $y")
|
||||||
|
params["y"] = *req.Y
|
||||||
|
}
|
||||||
|
|
||||||
|
query := `MATCH (n {id: $id})`
|
||||||
|
|
||||||
|
// 处理自定义属性(不管是否有标准字段更新)
|
||||||
|
if req.Properties != nil && len(req.Properties) > 0 {
|
||||||
|
for key, value := range req.Properties {
|
||||||
|
// 跳过系统属性,避免冲突
|
||||||
|
if key != "id" && key != "label" && key != "type" && key != "x" && key != "y" {
|
||||||
|
setClauses = append(setClauses, fmt.Sprintf("n.%s = $%s", key, key))
|
||||||
|
params[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(setClauses) > 0 {
|
||||||
|
query += ` SET ` + fmt.Sprintf("%s", strings.Join(setClauses, ", "))
|
||||||
|
}
|
||||||
|
query += ` RETURN n`
|
||||||
|
|
||||||
|
result, err := neo4j.ExecuteQuery(ctx, s.driver, query, params, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error updating node: %v\n", err)
|
||||||
|
return models.Node{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.Records) == 0 {
|
||||||
|
return models.Node{}, fmt.Errorf("failed to update node")
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := result.Records[0].Get("n")
|
||||||
|
if n, ok := v.(neo4j.Node); ok {
|
||||||
|
return neo4jNodeToModel(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.Node{}, fmt.Errorf("unexpected result type while updating node")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteNode 删除节点
|
||||||
|
func (s *Neo4jService) DeleteNode(id string) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 检查节点是否存在
|
||||||
|
_, exists := s.GetNodeByID(id)
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("node with id %s not found", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先删除与该节点相关的所有关系
|
||||||
|
deleteRelationsQuery := `
|
||||||
|
MATCH (n {id: $id})-[r]-(m)
|
||||||
|
DELETE r`
|
||||||
|
|
||||||
|
_, err := neo4j.ExecuteQuery(ctx, s.driver, deleteRelationsQuery,
|
||||||
|
map[string]any{"id": id}, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error deleting relations: %v\n", err)
|
||||||
|
// 继续尝试删除节点
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除节点
|
||||||
|
deleteNodeQuery := `
|
||||||
|
MATCH (n {id: $id})
|
||||||
|
DELETE n`
|
||||||
|
|
||||||
|
_, err = neo4j.ExecuteQuery(ctx, s.driver, deleteNodeQuery,
|
||||||
|
map[string]any{"id": id}, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error deleting node: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEdge 创建新边
|
||||||
|
func (s *Neo4jService) CreateEdge(req models.CreateEdgeRequest) (models.Edge, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 检查源节点和目标节点是否存在
|
||||||
|
_, sourceExists := s.GetNodeByID(req.Source)
|
||||||
|
if !sourceExists {
|
||||||
|
return models.Edge{}, fmt.Errorf("source node with id %s not found", req.Source)
|
||||||
|
}
|
||||||
|
_, targetExists := s.GetNodeByID(req.Target)
|
||||||
|
if !targetExists {
|
||||||
|
return models.Edge{}, fmt.Errorf("target node with id %s not found", req.Target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建创建属性
|
||||||
|
|
||||||
|
// 构建创建属性
|
||||||
|
props := map[string]any{
|
||||||
|
"id": req.ID,
|
||||||
|
"label": req.Label,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并自定义属性
|
||||||
|
if req.Properties != nil {
|
||||||
|
for k, v := range req.Properties {
|
||||||
|
if k != "id" && k != "label" {
|
||||||
|
props[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定边类型
|
||||||
|
relationType := "RELATED_TO"
|
||||||
|
if req.Type != "" {
|
||||||
|
relationType = strings.ToUpper(req.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行创建
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
MATCH (a {id: $source}), (b {id: $target})
|
||||||
|
CREATE (a)-[r:%s]->(b)
|
||||||
|
SET r = $props
|
||||||
|
RETURN r`, relationType)
|
||||||
|
|
||||||
|
params := map[string]any{
|
||||||
|
"source": req.Source,
|
||||||
|
"target": req.Target,
|
||||||
|
"props": props,
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := neo4j.ExecuteQuery(ctx, s.driver, query, params, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error creating edge: %v\n", err)
|
||||||
|
return models.Edge{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.Records) == 0 {
|
||||||
|
return models.Edge{}, fmt.Errorf("failed to create edge")
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := result.Records[0].Get("r")
|
||||||
|
if r, ok := v.(neo4j.Relationship); ok {
|
||||||
|
edge := neo4jRelToModel(r)
|
||||||
|
edge.Source = req.Source
|
||||||
|
edge.Target = req.Target
|
||||||
|
return edge, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.Edge{}, fmt.Errorf("unexpected result type while creating edge")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEdge 删除边
|
||||||
|
func (s *Neo4jService) DeleteEdge(edgeID string) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// 检查边是否存在
|
||||||
|
checkQuery := `
|
||||||
|
MATCH ()-[r]-()
|
||||||
|
WHERE r.id = $id
|
||||||
|
RETURN r`
|
||||||
|
checkResult, err := neo4j.ExecuteQuery(ctx, s.driver, checkQuery,
|
||||||
|
map[string]any{"id": edgeID}, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(checkResult.Records) == 0 {
|
||||||
|
return fmt.Errorf("edge with id %s not found", edgeID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除边
|
||||||
|
deleteQuery := `
|
||||||
|
MATCH ()-[r]-()
|
||||||
|
WHERE r.id = $id
|
||||||
|
DELETE r`
|
||||||
|
|
||||||
|
_, err = neo4j.ExecuteQuery(ctx, s.driver, deleteQuery,
|
||||||
|
map[string]any{"id": edgeID}, neo4j.EagerResultTransformer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error deleting edge: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,4 +8,12 @@ type GraphService interface {
|
|||||||
SearchNodes(query string) []models.Node
|
SearchNodes(query string) []models.Node
|
||||||
GetNeighbors(nodeID string) (models.NeighborResponse, bool)
|
GetNeighbors(nodeID string) (models.NeighborResponse, bool)
|
||||||
GetStats() map[string]int
|
GetStats() map[string]int
|
||||||
|
|
||||||
|
// 新增增删改接口
|
||||||
|
CreateNode(req models.CreateNodeRequest) (models.Node, error)
|
||||||
|
UpdateNode(id string, req models.UpdateNodeRequest) (models.Node, error)
|
||||||
|
DeleteNode(id string) error
|
||||||
|
|
||||||
|
CreateEdge(req models.CreateEdgeRequest) (models.Edge, error)
|
||||||
|
DeleteEdge(id string) error
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user