知识图谱导出方案

从 Obsidian Markdown Vault 到结构化数据库的完整导出流程设计。


架构总览

Obsidian Vault (Markdown + YAML)
        │
        ├── 1. 提取阶段 (Extract)
        │   ├── frontmatter 解析 → JSON
        │   ├── 双向链接 [[...]] 解析 → 关系图
        │   └── 标签 #tag 收集 → 标签索引
        │
        ├── 2. 转换阶段 (Transform)
        │   ├── 数据清洗 / 去重
        │   ├── 格式标准化
        │   └── 关系验证
        │
        ├── 3. 加载阶段 (Load)
        │   ├── PostgreSQL (结构化数据)
        │   └── Neo4j (知识图谱)
        │
        └── 4. 持续同步 (Sync)
            ├── Git Hook 触发
            └── 增量更新

Phase 1: 提取脚本

Python 脚本: tools/extract.py

import yaml
import frontmatter
import os
import json
from pathlib import Path
 
def extract_vault(vault_path: str, output_path: str):
    """解析 Obsidian Vault 所有条目为结构化 JSON"""
    data = {
        "hexagrams": [],
        "concepts": [],
        "classics": [],
        "persons": [],
        "methods": [],
        "relations": []
    }
    
    for md_file in Path(vault_path).rglob("*.md"):
        if ".obsidian" in str(md_file) or "模板" in str(md_file):
            continue
        
        post = frontmatter.load(md_file)
        meta = post.metadata
        content = post.content
        
        # 提取双向链接
        links = re.findall(r'\[\[([^\]]+)\]\]', content)
        
        # 按 id 类型分类到对应集合
        if meta.get('id', '').startswith('hexagram-'):
            data['hexagrams'].append(meta)
        elif meta.get('id', '').startswith('concept-'):
            data['concepts'].append(meta)
        elif meta.get('id', '').startswith('classic-'):
            data['classics'].append(meta)
        elif meta.get('id', '').startswith('person-'):
            data['persons'].append(meta)
        elif meta.get('id', '').startswith('method-'):
            data['methods'].append(meta)
    
    # 导出
    for key, items in data.items():
        with open(f"{output_path}/{key}.json", "w") as f:
            json.dump(items, f, ensure_ascii=False, indent=2)
    
    return data

链接解析规则

链接模式含义映射为关系
[[卦名]]双向链接N:N 关系
[[人物名|显示名]]带别名的链接N:N 关系
#tag标签标签索引
[[]] 空链接待填充的占位符忽略

Phase 2: 数据库 Migration

PostgreSQL Schema (核心表)

-- 卦象表
CREATE TABLE hexagrams (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    symbol      TEXT,
    order_num   INT,
    upper_trigram_id TEXT REFERENCES trigrams(id),
    lower_trigram_id TEXT REFERENCES trigrams(id),
    gua_ci      TEXT,
    tuan_ci     TEXT,
    xiang_ci    TEXT,
    yao_ci      JSONB,
    wuxing      TEXT,
    direction   TEXT,
    keywords    TEXT[],
    sources     JSONB,
    created_at  TIMESTAMPTZ DEFAULT NOW(),
    updated_at  TIMESTAMPTZ DEFAULT NOW()
);
 
-- 八卦表
CREATE TABLE trigrams (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    symbol      TEXT,
    binary      TEXT,
    wuxing      TEXT,
    direction_pre_heaven TEXT,
    direction_post_heaven TEXT
);
 
-- 概念表
CREATE TABLE concepts (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    category    TEXT,
    definition  TEXT,
    super_concepts JSONB,
    sub_concepts   JSONB,
    related_concepts JSONB,
    classic_quotes JSONB,
    keywords    TEXT[]
);
 
-- 典籍表
CREATE TABLE classics (
    id          TEXT PRIMARY KEY,
    title       TEXT NOT NULL,
    authors     JSONB,
    dynasty     TEXT,
    school      TEXT[],
    volumes     INT,
    collection  TEXT,
    source_urls JSONB,
    structure   JSONB
);
 
-- 人物表
CREATE TABLE persons (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    courtesy_name TEXT,
    birth_year  INT,
    death_year  INT,
    dynasty     TEXT,
    school      TEXT[],
    cbdb_id     TEXT,
    works       JSONB
);
 
-- 术数表
CREATE TABLE methods (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    type        TEXT,
    theory_basis JSONB,
    classic_sources JSONB,
    steps       JSONB
);
 
-- 关系表 (泛化关系表)
CREATE TABLE relations (
    id          SERIAL PRIMARY KEY,
    source_id   TEXT NOT NULL,
    target_id   TEXT NOT NULL,
    type        TEXT NOT NULL,
    properties  JSONB,
    UNIQUE(source_id, target_id, type)
);

Neo4j 导入脚本

// 使用 APOC 从 JSON 批量导入
CALL apoc.load.json('file:///export/hexagrams.json') 
YIELD value
CREATE (h:Hexagram {
    id: value.id,
    name: value.name,
    symbol: value.symbol,
    order: value.order_num,
    gua_ci: value.gua_ci,
    wuxing: value.wuxing
});
 
// 建立关系
CALL apoc.load.json('file:///export/relations.json')
YIELD value
MATCH (a {id: value.source_id})
MATCH (b {id: value.target_id})
CALL apoc.create.relationship(a, value.type, {}, b) YIELD rel
RETURN count(*);

Phase 3: 持续同步

Git Hook 方案

#!/bin/bash
# .git/hooks/post-commit
 
echo "🔄 检测到提交,触发 JSON 导出..."
python3 tools/extract.py \
    --vault . \
    --output export/
echo "✅ 导出完成"
 
# 可选:自动部署到数据库
# python3 tools/deploy.py

增量更新策略

  1. 基于文件 mtime:只重新解析修改过的 .md 文件
  2. 计算 Hash:对每个文件的 YAML frontmatter 计算 SHA256,缺省仅更新变更条目
  3. Obsidian Git 插件:自动提交,触发 Git Hook 联动导出

技术选型

组件技术说明
解析器python-frontmatter解析 YAML Frontmatter
关系提取Python re正则提取 链接
ETL 编排Python + Click命令行工具
关系数据库PostgreSQL 16+JSONB 支持良好
图数据库Neo4j 5.xCypher 查询,知识图谱可视化
API 框架FastAPI (Python)异步支持,自动生成 OpenAPI
容器化Docker Compose一键部署
CI/CDGitHub Actions自动导出 + 部署

后续步骤

  1. 实现 tools/extract.py 解析脚本
  2. 编写 PostgreSQL Migration 文件
  3. 编写 Neo4j 导入脚本
  4. 搭建 FastAPI 项目骨架
  5. 编写 Docker Compose 部署配置
  6. 编写 GitHub Actions CI/CD Workflow