我已经搭建好了neo4j,告诉我完整步骤和代码搭建MVP把项目运行起来,实现把圣经中各个人物,地点,事件以及时间的关系呈现出来

本文最后更新于 2025年12月3日。

太棒了!既然您已经安装好了 Neo4j,我们现在就通过一个最小可行性产品 (MVP),把圣经中的“亚伯拉罕家族迁移”作为案例,完整实现从数据录入到网页可视化的全过程。

我们将采用 Python (Flask) 做后端 API,Vis.js 做前端可视化(这是最适合展示复杂关系图的库)。


📂 项目文件结构

请在电脑上创建一个文件夹 BibleGraphMVP,结构如下:

Plaintext

BibleGraphMVP/
├── app.py              # Python 后端代码
├── templates/
│   └── index.html      # 前端网页代码
└── data/               # 存放 CSV 数据文件
    ├── people.csv
    ├── places.csv
    ├── events.csv
    └── relationships.csv

第一步:准备数据 (CSV)

为了演示,我们手动创建一小部分数据。请在 data 文件夹下创建以下 4 个 CSV 文件。

1. people.csv (人物)

Code snippet

id,name,name_cn
p1,Abraham,亚伯拉罕
p2,Sarah,撒拉
p3,Isaac,以撒

2. places.csv (地点 – 含坐标)

Code snippet

id,name,lat,long
l1,Ur,30.96,46.10
l2,Haran,36.86,39.02
l3,Canaan,31.50,34.80

3. events.csv (事件 – 含时间)

Code snippet

id,title,year
e1,Call of Abram,-2091
e2,Birth of Isaac,-2066
  1. relationships.csv (关系 – 核心)

这里定义了人与人、人与事件、事件与地点的关系。

Code snippet

source,target,type
p1,p2,SPOUSE          # 亚伯拉罕 配偶 撒拉
p1,p3,FATHER_OF       # 亚伯拉罕 父亲 以撒
p2,p3,MOTHER_OF       # 撒拉 母亲 以撒
p1,e1,PARTICIPATED_IN # 亚伯拉罕 参与 呼召事件
e1,l1,STARTED_AT      # 呼召事件 开始于 吾珥
e1,l2,MOVED_TO        # 呼召事件 迁移到 哈兰
p3,e2,SUBJECT_OF      # 以撒 是 出生事件的主角
e2,l3,HAPPENED_AT     # 出生事件 发生于 迦南

⚠️ 重要操作: 将这 4 个 CSV 文件复制到您 Neo4j 安装目录下的 import 文件夹中(例如 C:\Users\...\.Neo4jDesktop\relate-data\dbmss\...\import)。这是 Neo4j 默认读取文件的位置。


第二步:数据导入 (Cypher)

打开 Neo4j Browser (http://localhost:7474),依次运行以下 Cypher 语句将数据加载进数据库:

Cypher

// 1. 加载人物
LOAD CSV WITH HEADERS FROM 'file:///people.csv' AS row
MERGE (p:Person {id: row.id})
SET p.name = row.name, p.name_cn = row.name_cn;

// 2. 加载地点
LOAD CSV WITH HEADERS FROM 'file:///places.csv' AS row
MERGE (l:Location {id: row.id})
SET l.name = row.name, l.lat = toFloat(row.lat), l.long = toFloat(row.long);

// 3. 加载事件
LOAD CSV WITH HEADERS FROM 'file:///events.csv' AS row
MERGE (e:Event {id: row.id})
SET e.title = row.title, e.year = toInteger(row.year);

// 4. 加载关系 (动态匹配)
LOAD CSV WITH HEADERS FROM 'file:///relationships.csv' AS row
MATCH (s {id: row.source})
MATCH (t {id: row.target})
CALL apoc.create.relationship(s, row.type, {}, t) YIELD rel
RETURN count(rel);

(注:如果第4步报错提示找不到 apoc,可以用标准的 MERGE 语句代替,但为了MVP简单,我们假设您能分别处理,或者手动写几条 MERGE 语句。最简单的标准写法如下:)

替代第4步(标准写法,无插件):

Cypher

LOAD CSV WITH HEADERS FROM 'file:///relationships.csv' AS row
MATCH (s {id: row.source})
MATCH (t {id: row.target})
FOREACH (ignoreMe IN CASE WHEN row.type = 'SPOUSE' THEN [1] ELSE [] END | MERGE (s)-[:SPOUSE]->(t))
FOREACH (ignoreMe IN CASE WHEN row.type = 'FATHER_OF' THEN [1] ELSE [] END | MERGE (s)-[:FATHER_OF]->(t))
FOREACH (ignoreMe IN CASE WHEN row.type = 'MOTHER_OF' THEN [1] ELSE [] END | MERGE (s)-[:MOTHER_OF]->(t))
FOREACH (ignoreMe IN CASE WHEN row.type = 'PARTICIPATED_IN' THEN [1] ELSE [] END | MERGE (s)-[:PARTICIPATED_IN]->(t))
FOREACH (ignoreMe IN CASE WHEN row.type = 'STARTED_AT' THEN [1] ELSE [] END | MERGE (s)-[:STARTED_AT]->(t))
FOREACH (ignoreMe IN CASE WHEN row.type = 'MOVED_TO' THEN [1] ELSE [] END | MERGE (s)-[:MOVED_TO]->(t))
FOREACH (ignoreMe IN CASE WHEN row.type = 'SUBJECT_OF' THEN [1] ELSE [] END | MERGE (s)-[:SUBJECT_OF]->(t))
FOREACH (ignoreMe IN CASE WHEN row.type = 'HAPPENED_AT' THEN [1] ELSE [] END | MERGE (s)-[:HAPPENED_AT]->(t));

第三步:编写后端 (Python Flask)

安装依赖:

Bash

pip install flask neo4j

创建 app.py

Python

from flask import Flask, render_template, jsonify
from neo4j import GraphDatabase

app = Flask(__name__)

# 配置 Neo4j 连接 (修改为你的密码)
URI = "bolt://localhost:7687"
AUTH = ("neo4j", "your_password") 

driver = GraphDatabase.driver(URI, auth=AUTH)

def get_graph_data():
    query = """
    MATCH (n)-[r]->(m)
    RETURN n, r, m
    LIMIT 100
    """
    nodes = {}
    edges = []
    
    with driver.session() as session:
        result = session.run(query)
        for record in result:
            # 处理源节点
            n_node = record["n"]
            n_id = n_node.get("id") or n_node.element_id
            # 根据标签设置显示名称
            label = list(n_node.labels)[0]
            if label == "Person": label_prop = n_node.get("name_cn")
            elif label == "Event": label_prop = f"{n_node.get('title')} ({n_node.get('year')})"
            else: label_prop = n_node.get("name")
            
            # 使用简单的颜色区分节点类型
            color = "#97C2FC" # 默认蓝
            if label == "Person": color = "#FFD700" # 金色
            if label == "Event": color = "#FF6347"  # 红色
            if label == "Location": color = "#90EE90" # 绿色

            nodes[n_id] = {
                "id": n_id, 
                "label": label_prop,
                "group": label,
                "color": color,
                "title": str(dict(n_node)) # 鼠标悬停显示详细信息
            }
            
            # 处理目标节点 (同上逻辑,简化处理)
            m_node = record["m"]
            m_id = m_node.get("id") or m_node.element_id
            m_label = list(m_node.labels)[0]
            if m_label == "Person": m_show = m_node.get("name_cn")
            elif m_label == "Event": m_show = f"{m_node.get('title')} ({m_node.get('year')})"
            else: m_show = m_node.get("name")
            
            m_color = "#97C2FC"
            if m_label == "Person": m_color = "#FFD700"
            if m_label == "Event": m_color = "#FF6347"
            if m_label == "Location": m_color = "#90EE90"

            nodes[m_id] = {
                "id": m_id, 
                "label": m_show, 
                "group": m_label,
                "color": m_color,
                "title": str(dict(m_node))
            }

            # 处理关系
            edges.append({
                "from": n_id,
                "to": m_id,
                "label": record["r"].type,
                "arrows": "to"
            })

    return {"nodes": list(nodes.values()), "edges": edges}

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/api/data')
def data():
    graph_data = get_graph_data()
    return jsonify(graph_data)

if __name__ == '__main__':
    app.run(debug=True, port=5000)

第四步:编写前端 (HTML + Vis.js)

创建 templates/index.html

HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>圣经关系可视化 MVP</title>
    <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
    <style>
        body { font-family: sans-serif; background-color: #f0f0f0; }
        #network {
            width: 100%;
            height: 800px;
            border: 1px solid lightgray;
            background-color: white;
        }
        .legend { margin: 10px; }
        span.dot { height: 10px; width: 10px; border-radius: 50%; display: inline-block; }
    </style>
</head>
<body>
    <h1>圣经人物、事件、地点关系图谱</h1>
    <div class="legend">
        <span class="dot" style="background-color: #FFD700;"></span> 人物
        <span class="dot" style="background-color: #FF6347;"></span> 事件 (含时间)
        <span class="dot" style="background-color: #90EE90;"></span> 地点
    </div>
    <div id="network">加载中...</div>

    <script type="text/javascript">
        // 从后端 API 获取数据
        fetch('/api/data')
            .then(response => response.json())
            .then(data => {
                var container = document.getElementById('network');
                
                var options = {
                    nodes: {
                        shape: 'dot',
                        size: 20,
                        font: { size: 14 }
                    },
                    edges: {
                        font: { size: 12, align: 'middle' },
                        color: 'gray',
                        smooth: { type: 'continuous' }
                    },
                    physics: {
                        stabilization: false,
                        barnesHut: { gravitationalConstant: -8000 }
                    }
                };

                // 初始化网络图
                var network = new vis.Network(container, data, options);
            })
            .catch(error => console.error('Error:', error));
    </script>
</body>
</html>

第五步:运行项目 🚀

  1. 启动后端: 在终端中运行 python app.py

  2. 访问网页: 打开浏览器访问 http://127.0.0.1:5000

🎉 您将看到什么?

您会看到一个交互式的网络图:

  • 黄色节点是亚伯拉罕、撒拉和以撒。

  • 红色节点是“呼召”和“出生”事件,上面标注了年份(如 -2091)。

  • 绿色节点是吾珥、哈兰和迦南。

  • 连线清晰地展示了:“亚伯拉罕” -> 参与了 -> “呼召事件” -> 发生于 -> “吾珥”。

这就完成了一个最基础的 MVP!您可以随时往 CSV 里添加更多数据,重新运行 Cypher 导入语句,刷新网页即可看到扩充后的图谱。

验证关系是否创建成功

MATCH ()-[r]->() RETURN count(r);