[js] 代码片段分享 - 思源关系图谱的深度支持

昨天我花了点时间使用 cursor 写了一个简单的增强本地图谱的代码片段,也给大家分享一下

主要实现了两个功能:

  1. 本地图谱支持深度
  2. 出链也会计算权重

效果

代码片段关闭:

image.png

image.png

代码片段开启:

image.png

image.png

这个片段可以用来干什么

  • 一个用于快速跳转的路径地图(这个用处最大了,文档树、搜索、反链面板都去一边吧)
  • 打开一个文档,就可以通过关系图谱快速记起相关联项目的结构

本来还编了很多的,想了想还是删了。

代码

(function() {
    // 预设配置 - 可根据需要调整
    const 预设配置 = {
        默认: {
            最大深度: 4,             // 建议3-4层深度对大多数情况已足够
            每层最大节点数: 25,       // 每层处理的最大节点数
            并发请求数: 8,           // 同时发送的最大请求数
            请求间隔: 0,             // 请求间隔(毫秒),0表示不延迟
            最大处理总数: 120         // 处理节点上限
        },
        高速: {
            最大深度: 4,             // 限制深度提高响应速度
            每层最大节点数: 15,       // 减少每层节点数加快处理
            并发请求数: 15,          // 大幅提高并发
            请求间隔: 0,             // 无延迟
            最大处理总数: 100         // 减少处理总数提高速度
        },
        全面: {
            最大深度: 5,             // 增加深度获取更多关联
            每层最大节点数: 30,       // 增加每层节点数获取更全面信息
            并发请求数: 6,           // 降低并发减轻服务器压力
            请求间隔: 50,            // 添加少量延迟
            最大处理总数: 150         // 处理更多节点
        }
    };

    // 当前使用的配置(默认使用"高速"配置)
    const 当前模式 = "高速";
  
    // 运行配置
    const 配置 = {
        ...预设配置[当前模式],
        已处理节点: new Set(),    // 已处理节点集合
        已请求节点: new Set(),    // 已请求节点集合
        正在处理: false,         // 处理标记
        显示调试信息: true,       // 是否显示调试信息
        节点缓存: new Map(),      // 节点请求结果缓存
        处理计数: 0,             // 处理计数器
        双向调整权重: true,       // 是否为引用和被引用节点都调整大小
        默认节点大小: 15,         // 默认节点大小
        引用权重系数: 1.0,        // 引用其他节点时的权重系数
        被引用权重系数: 1.2,       // 被引用节点时的权重系数(略高于引用权重)
        使用线性计算: true,        // 使用线性计算而非对数计算
        大小增长系数: 0.7,         // 节点大小增长系数(值越大,差异越明显)
        尺寸上限倍数: 5.0,         // 节点最大尺寸为默认尺寸的倍数
        强化对比度: true,          // 进一步强化节点大小差异
        基础增长倍数: 1.5          // 即使只有1个引用也会有的基础增长
    };

    // 保存原始fetch函数
    const 原始Fetch = window.fetch;
  
    // 拦截fetch请求
    window.fetch = async function(...args) {
        const url = args[0];
  
        // 只拦截关系图请求
        if (typeof url === 'string' && url.includes('/api/graph/getLocalGraph')) {
            // 避免递归处理
            if (配置.正在处理) {
                return 原始Fetch.apply(this, args);
            }
    
            try {
                配置.正在处理 = true;
        
                // 获取基础数据
                const 响应 = await 原始Fetch.apply(this, args);
                const 响应克隆 = 响应.clone();
                const 原始数据 = await 响应克隆.json();
        
                // 获取当前文档ID
                const 请求体 = JSON.parse(args[1].body);
                const 根节点ID = 请求体.id;
        
                // 重置状态
                配置.已处理节点.clear();
                配置.已请求节点.clear();
                配置.节点缓存.clear();
                配置.处理计数 = 0;
        
                if (配置.显示调试信息) {
                    console.log(`[图谱增强${当前模式}] 开始处理根节点`);
                    console.time('图谱处理用时');
                }
        
                // 初始化根节点
                配置.已处理节点.add(根节点ID);
                配置.已请求节点.add(根节点ID);
                配置.处理计数++;
        
                // 准备数据结构 - 直接使用Map提高查找效率
                const 节点映射 = new Map(原始数据.data.nodes.map(节点 => [节点.id, 节点]));
                const 连线映射 = new Map();
        
                // 引用计数器 - 记录每个节点的引用和被引用次数
                const 引用计数 = new Map();
        
                // 初始化引用计数
                for (const 节点 of 原始数据.data.nodes) {
                    引用计数.set(节点.id, { 引用数: 0, 被引用数: 0 });
                }
        
                // 记录连线 - 使用Set加速
                const 连线集合 = new Set();
                原始数据.data.links.forEach(连线 => {
                    const 连线键 = `${连线.from}-${连线.to}`;
                    连线映射.set(连线键, 连线);
                    连线集合.add(连线键);
            
                    // 更新引用计数
                    if (引用计数.has(连线.from)) {
                        const 计数 = 引用计数.get(连线.from);
                        计数.引用数 += 1;
                        引用计数.set(连线.from, 计数);
                    }
            
                    if (引用计数.has(连线.to)) {
                        const 计数 = 引用计数.get(连线.to);
                        计数.被引用数 += 1;
                        引用计数.set(连线.to, 计数);
                    }
                });
        
                // 快速找出初始关联节点
                const 初始关联节点 = [];
                for (const 连线 of 原始数据.data.links) {
                    const 相关节点 = 连线.from === 根节点ID ? 连线.to : 
                                 连线.to === 根节点ID ? 连线.from : null;
                    if (相关节点 && !配置.已请求节点.has(相关节点)) {
                        初始关联节点.push(相关节点);
                        配置.已请求节点.add(相关节点);
                    }
                }
        
                // 高性能处理所有层级
                await 高速处理(初始关联节点, 节点映射, 连线映射, 连线集合, 引用计数, 请求体.conf);
        
                // 计算最终节点大小
                if (配置.双向调整权重) {
                    重新计算节点大小(节点映射, 引用计数);
                }
        
                if (配置.显示调试信息) {
                    console.timeEnd('图谱处理用时');
                    console.log(`[图谱增强${当前模式}] 完成: ${节点映射.size}节点, ${连线映射.size}连接, 处理${配置.处理计数}节点`);
                }
        
                // 构建新响应
                const 新数据 = {
                    ...原始数据,
                    data: {
                        ...原始数据.data,
                        nodes: Array.from(节点映射.values()),
                        links: Array.from(连线映射.values())
                    }
                };
        
                return new Response(JSON.stringify(新数据), {
                    status: 响应.status,
                    statusText: 响应.statusText,
                    headers: 响应.headers
                });
            } finally {
                配置.正在处理 = false;
            }
        }
  
        return 原始Fetch.apply(this, args);
    };
  
    /**
     * 从连线中提取相关节点
     */
    function 获取关联节点(节点ID, 连线数组, 已访问集合) {
        const 关联节点 = [];
        for (const 连线 of 连线数组) {
            if (连线.from === 节点ID && !已访问集合.has(连线.to)) {
                关联节点.push(连线.to);
            } else if (连线.to === 节点ID && !已访问集合.has(连线.from)) {
                关联节点.push(连线.from);
            }
        }
        return 关联节点;
    }
  
    /**
     * 高性能处理所有层级节点
     */
    async function 高速处理(初始节点集, 节点映射, 连线映射, 连线集合, 引用计数, 图谱配置) {
        let 当前深度 = 1;
        let 当前层节点 = [...初始节点集];
  
        // 非递归批量处理多层节点
        while (
            当前深度 < 配置.最大深度 && 
            当前层节点.length > 0 && 
            配置.处理计数 < 配置.最大处理总数
        ) {
            if (配置.显示调试信息) {
                console.log(`[图谱增强${当前模式}] 处理第${当前深度}层: ${当前层节点.length}个节点, 已处理${配置.处理计数}个`);
            }
    
            // 限制本层处理量
            const 本层节点 = 当前层节点.slice(0, 配置.每层最大节点数);
            const 下一层节点集 = new Set();
    
            // 高并发批处理
            for (let i = 0; i < 本层节点.length; i += 配置.并发请求数) {
                // 分批处理,每批并发请求
                const 批次节点 = 本层节点.slice(i, i + 配置.并发请求数);
        
                // 并行处理当前批次
                const 批处理结果 = await Promise.all(
                    批次节点.map(async (节点ID) => {
                        // 跳过已处理
                        if (配置.已处理节点.has(节点ID) || 配置.处理计数 >= 配置.最大处理总数) {
                            return null;
                        }
                
                        try {
                            // 标记处理
                            配置.已处理节点.add(节点ID);
                            配置.处理计数++;
                    
                            // 优先使用缓存
                            if (配置.节点缓存.has(节点ID)) {
                                return 配置.节点缓存.get(节点ID);
                            }
                    
                            // 发送请求
                            const 请求数据 = {
                                type: "local", 
                                k: "",
                                id: 节点ID,
                                conf: 图谱配置
                            };
                    
                            const 响应 = await fetch('/api/graph/getLocalGraph', {
                                method: 'POST',
                                headers: {'Content-Type': 'application/json'},
                                body: JSON.stringify(请求数据)
                            });
                    
                            const 响应数据 = await 响应.json();
                    
                            // 处理数据
                            if (响应数据?.code === 0 && 响应数据.data) {
                                // 提取下一级关联节点
                                const 相关节点 = [];
                        
                                // 快速合并节点
                                for (const 节点 of 响应数据.data.nodes) {
                                    if (!节点映射.has(节点.id)) {
                                        节点映射.set(节点.id, 节点);
                                
                                        // 初始化引用计数
                                        if (!引用计数.has(节点.id)) {
                                            引用计数.set(节点.id, { 引用数: 0, 被引用数: 0 });
                                        }
                                    }
                                }
                        
                                // 快速合并连线并收集关联节点
                                for (const 连线 of 响应数据.data.links) {
                                    const 连线键 = `${连线.from}-${连线.to}`;
                                    if (!连线集合.has(连线键)) {
                                        连线集合.add(连线键);
                                        连线映射.set(连线键, 连线);
                                
                                        // 更新引用计数
                                        if (引用计数.has(连线.from)) {
                                            const 计数 = 引用计数.get(连线.from);
                                            计数.引用数 += 1;
                                            引用计数.set(连线.from, 计数);
                                        } else {
                                            引用计数.set(连线.from, { 引用数: 1, 被引用数: 0 });
                                        }
                                
                                        if (引用计数.has(连线.to)) {
                                            const 计数 = 引用计数.get(连线.to);
                                            计数.被引用数 += 1;
                                            引用计数.set(连线.to, 计数);
                                        } else {
                                            引用计数.set(连线.to, { 引用数: 0, 被引用数: 1 });
                                        }
                                
                                        // 提取关联节点
                                        const 目标节点 = 连线.from === 节点ID ? 连线.to : 
                                                   连线.to === 节点ID ? 连线.from : null;
                                        if (目标节点 && !配置.已请求节点.has(目标节点)) {
                                            相关节点.push(目标节点);
                                            配置.已请求节点.add(目标节点);
                                        }
                                    }
                                }
                        
                                // 缓存结果
                                配置.节点缓存.set(节点ID, 相关节点);
                                return 相关节点;
                            }
                    
                            return null;
                        } catch (错误) {
                            return null;
                        }
                    })
                );
        
                // 合并到下一层
                for (const 结果 of 批处理结果) {
                    if (结果 && Array.isArray(结果)) {
                        结果.forEach(id => 下一层节点集.add(id));
                    }
                }
        
                // 批次间可选延迟
                if (配置.请求间隔 > 0 && i + 配置.并发请求数 < 本层节点.length) {
                    await new Promise(resolve => setTimeout(resolve, 配置.请求间隔));
                }
            }
    
            // 准备下一层
            当前层节点 = Array.from(下一层节点集);
            当前深度++;
        }
    }
  
    /**
     * 重新计算节点大小,同时考虑引用和被引用因素,使用更合理的计算方式
     * 增强节点大小差异,使重要节点更加突出
     */
    function 重新计算节点大小(节点映射, 引用计数) {
        // 找出最大的引用数和被引用数,用于归一化
        let 最大引用数 = 1;
        let 最大被引用数 = 1;
  
        for (const 计数 of 引用计数.values()) {
            最大引用数 = Math.max(最大引用数, 计数.引用数);
            最大被引用数 = Math.max(最大被引用数, 计数.被引用数);
        }
  
        // 防止除零
        最大引用数 = Math.max(1, 最大引用数);
        最大被引用数 = Math.max(1, 最大被引用数);
  
        for (const [节点ID, 节点对象] of 节点映射.entries()) {
            if (引用计数.has(节点ID)) {
                const 计数 = 引用计数.get(节点ID);
        
                let 引用得分, 被引用得分;
        
                if (配置.使用线性计算) {
                    // 线性计算得分 - 更公平地表示引用数量
                    引用得分 = (计数.引用数 / 最大引用数) * 配置.引用权重系数;
                    被引用得分 = (计数.被引用数 / 最大被引用数) * 配置.被引用权重系数;
            
                    // 强化对比度 - 让即使很少引用的节点也有明显变化
                    if (配置.强化对比度 && (计数.引用数 > 0 || 计数.被引用数 > 0)) {
                        引用得分 = 配置.基础增长倍数 * 引用得分 / (引用得分 + 0.3);
                        被引用得分 = 配置.基础增长倍数 * 被引用得分 / (被引用得分 + 0.3);
                    }
                } else {
                    // 对数计算得分 - 平滑处理引用数较多的情况
                    引用得分 = Math.log2(计数.引用数 + 1) / Math.log2(最大引用数 + 1) * 配置.引用权重系数;
                    被引用得分 = Math.log2(计数.被引用数 + 1) / Math.log2(最大被引用数 + 1) * 配置.被引用权重系数;
                }
        
                // 合并得分,计算最终尺寸
                const 总得分 = 引用得分 + 被引用得分;
        
                // 使用更激进的计算方式增大差异
                let 尺寸系数;
        
                if (配置.强化对比度) {
                    // 指数增长模式,让差异更明显
                    尺寸系数 = Math.pow(1 + 总得分, 配置.大小增长系数 * 2);
                } else {
                    // 缩放到合理范围内
                    尺寸系数 = 1 + (总得分 * 配置.大小增长系数 * 配置.尺寸上限倍数);
                }
        
                // 限制最大尺寸
                const 最终系数 = Math.min(尺寸系数, 配置.尺寸上限倍数);
        
                // 应用新尺寸
                节点对象.size = 配置.默认节点大小 * 最终系数;
        
                // 添加引用和被引用数据便于调试
                节点对象.refs = 计数.引用数;
                节点对象.defs = 计数.被引用数;
        
                // 如果是调试模式,为节点添加权重信息
                if (配置.显示调试信息 && (计数.引用数 > 0 || 计数.被引用数 > 0)) {
                    const 原标签 = 节点对象.label;
                    节点对象.title = `${原标签}\n引用: ${计数.引用数}, 被引用: ${计数.被引用数}`;
                }
            }
        }
    }
  
    // 显示已启用通知
    console.log(`[图谱深度增强] 已启用${当前模式}模式: 深度${配置.最大深度}, 节点上限${配置.最大处理总数}, 强化对比${配置.强化对比度}`);
  
    // 添加样式
    const 样式 = document.createElement('style');
    样式.textContent = `
        .graph-depth-notification {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background-color: rgba(0, 0, 0, 0.75);
            color: white;
            padding: 12px 16px;
            border-radius: 6px;
            z-index: 9999;
            font-size: 14px;
            transition: opacity 0.5s;
            opacity: 1;
        }
        .graph-depth-notification.fade {
            opacity: 0;
        }
    `;
    document.head.appendChild(样式);
  
    // 创建通知
    const 通知 = document.createElement('div');
    通知.className = 'graph-depth-notification';
    通知.textContent = `思源笔记关系图谱深度增强已启用 (${当前模式}模式, 强化节点差异)`;
    document.body.appendChild(通知);
  
    setTimeout(() => {
        通知.classList.add('fade');
        setTimeout(() => 通知.remove(), 500);
    }, 3000);
})(); 

再放个不会修改节点权重的旧版本

(function() {
    // 预设配置 - 可根据需要调整
    const 预设配置 = {
        默认: {
            最大深度: 4,             // 建议3-4层深度对大多数情况已足够
            每层最大节点数: 25,       // 每层处理的最大节点数
            并发请求数: 8,           // 同时发送的最大请求数
            请求间隔: 0,             // 请求间隔(毫秒),0表示不延迟
            最大处理总数: 120         // 处理节点上限
        },
        高速: {
            最大深度: 4,             // 限制深度提高响应速度
            每层最大节点数: 15,       // 减少每层节点数加快处理
            并发请求数: 15,          // 大幅提高并发
            请求间隔: 0,             // 无延迟
            最大处理总数: 100         // 减少处理总数提高速度
        },
        全面: {
            最大深度: 5,             // 增加深度获取更多关联
            每层最大节点数: 30,       // 增加每层节点数获取更全面信息
            并发请求数: 6,           // 降低并发减轻服务器压力
            请求间隔: 50,            // 添加少量延迟
            最大处理总数: 150         // 处理更多节点
        }
    };

    // 当前使用的配置(默认使用"高速"配置)
    const 当前模式 = "高速";
  
    // 运行配置
    const 配置 = {
        ...预设配置[当前模式],
        已处理节点: new Set(),   // 已处理节点集合
        已请求节点: new Set(),   // 已请求节点集合
        正在处理: false,        // 处理标记
        显示调试信息: true,      // 是否显示调试信息
        节点缓存: new Map(),     // 节点请求结果缓存
        处理计数: 0             // 处理计数器
    };

    // 保存原始fetch函数
    const 原始Fetch = window.fetch;
  
    // 拦截fetch请求
    window.fetch = async function(...args) {
        const url = args[0];
  
        // 只拦截关系图请求
        if (typeof url === 'string' && url.includes('/api/graph/getLocalGraph')) {
            // 避免递归处理
            if (配置.正在处理) {
                return 原始Fetch.apply(this, args);
            }
  
            try {
                配置.正在处理 = true;
    
                // 获取基础数据
                const 响应 = await 原始Fetch.apply(this, args);
                const 响应克隆 = 响应.clone();
                const 原始数据 = await 响应克隆.json();
    
                // 获取当前文档ID
                const 请求体 = JSON.parse(args[1].body);
                const 根节点ID = 请求体.id;
    
                // 重置状态
                配置.已处理节点.clear();
                配置.已请求节点.clear();
                配置.节点缓存.clear();
                配置.处理计数 = 0;
    
                if (配置.显示调试信息) {
                    console.log(`[图谱增强${当前模式}] 开始处理根节点`);
                    console.time('图谱处理用时');
                }
    
                // 初始化根节点
                配置.已处理节点.add(根节点ID);
                配置.已请求节点.add(根节点ID);
                配置.处理计数++;
    
                // 准备数据结构 - 直接使用Map提高查找效率
                const 节点映射 = new Map(原始数据.data.nodes.map(节点 => [节点.id, 节点]));
                const 连线映射 = new Map();
    
                // 记录连线 - 使用Set加速
                const 连线集合 = new Set();
                原始数据.data.links.forEach(连线 => {
                    const 连线键 = `${连线.from}-${连线.to}`;
                    连线映射.set(连线键, 连线);
                    连线集合.add(连线键);
                });
    
                // 快速找出初始关联节点
                const 初始关联节点 = [];
                for (const 连线 of 原始数据.data.links) {
                    const 相关节点 = 连线.from === 根节点ID ? 连线.to : 
                                 连线.to === 根节点ID ? 连线.from : null;
                    if (相关节点 && !配置.已请求节点.has(相关节点)) {
                        初始关联节点.push(相关节点);
                        配置.已请求节点.add(相关节点);
                    }
                }
    
                // 高性能处理所有层级
                await 高速处理(初始关联节点, 节点映射, 连线映射, 连线集合, 请求体.conf);
    
                if (配置.显示调试信息) {
                    console.timeEnd('图谱处理用时');
                    console.log(`[图谱增强${当前模式}] 完成: ${节点映射.size}节点, ${连线映射.size}连接, 处理${配置.处理计数}节点`);
                }
    
                // 构建新响应
                const 新数据 = {
                    ...原始数据,
                    data: {
                        ...原始数据.data,
                        nodes: Array.from(节点映射.values()),
                        links: Array.from(连线映射.values())
                    }
                };
    
                return new Response(JSON.stringify(新数据), {
                    status: 响应.status,
                    statusText: 响应.statusText,
                    headers: 响应.headers
                });
            } finally {
                配置.正在处理 = false;
            }
        }
  
        return 原始Fetch.apply(this, args);
    };
  
    /**
     * 从连线中提取相关节点
     */
    function 获取关联节点(节点ID, 连线数组, 已访问集合) {
        const 关联节点 = [];
        for (const 连线 of 连线数组) {
            if (连线.from === 节点ID && !已访问集合.has(连线.to)) {
                关联节点.push(连线.to);
            } else if (连线.to === 节点ID && !已访问集合.has(连线.from)) {
                关联节点.push(连线.from);
            }
        }
        return 关联节点;
    }
  
    /**
     * 高性能处理所有层级节点
     */
    async function 高速处理(初始节点集, 节点映射, 连线映射, 连线集合, 图谱配置) {
        let 当前深度 = 1;
        let 当前层节点 = [...初始节点集];
  
        // 非递归批量处理多层节点
        while (
            当前深度 < 配置.最大深度 && 
            当前层节点.length > 0 && 
            配置.处理计数 < 配置.最大处理总数
        ) {
            if (配置.显示调试信息) {
                console.log(`[图谱增强${当前模式}] 处理第${当前深度}层: ${当前层节点.length}个节点, 已处理${配置.处理计数}个`);
            }
  
            // 限制本层处理量
            const 本层节点 = 当前层节点.slice(0, 配置.每层最大节点数);
            const 下一层节点集 = new Set();
  
            // 高并发批处理
            for (let i = 0; i < 本层节点.length; i += 配置.并发请求数) {
                // 分批处理,每批并发请求
                const 批次节点 = 本层节点.slice(i, i + 配置.并发请求数);
    
                // 并行处理当前批次
                const 批处理结果 = await Promise.all(
                    批次节点.map(async (节点ID) => {
                        // 跳过已处理
                        if (配置.已处理节点.has(节点ID) || 配置.处理计数 >= 配置.最大处理总数) {
                            return null;
                        }
            
                        try {
                            // 标记处理
                            配置.已处理节点.add(节点ID);
                            配置.处理计数++;
                
                            // 优先使用缓存
                            if (配置.节点缓存.has(节点ID)) {
                                return 配置.节点缓存.get(节点ID);
                            }
                
                            // 发送请求
                            const 请求数据 = {
                                type: "local", 
                                k: "",
                                id: 节点ID,
                                conf: 图谱配置
                            };
                
                            const 响应 = await fetch('/api/graph/getLocalGraph', {
                                method: 'POST',
                                headers: {'Content-Type': 'application/json'},
                                body: JSON.stringify(请求数据)
                            });
                
                            const 响应数据 = await 响应.json();
                
                            // 处理数据
                            if (响应数据?.code === 0 && 响应数据.data) {
                                // 提取下一级关联节点
                                const 相关节点 = [];
                    
                                // 快速合并节点
                                for (const 节点 of 响应数据.data.nodes) {
                                    if (!节点映射.has(节点.id)) {
                                        节点映射.set(节点.id, 节点);
                                    }
                                }
                    
                                // 快速合并连线并收集关联节点
                                for (const 连线 of 响应数据.data.links) {
                                    const 连线键 = `${连线.from}-${连线.to}`;
                                    if (!连线集合.has(连线键)) {
                                        连线集合.add(连线键);
                                        连线映射.set(连线键, 连线);
                            
                                        // 提取关联节点
                                        const 目标节点 = 连线.from === 节点ID ? 连线.to : 
                                                   连线.to === 节点ID ? 连线.from : null;
                                        if (目标节点 && !配置.已请求节点.has(目标节点)) {
                                            相关节点.push(目标节点);
                                            配置.已请求节点.add(目标节点);
                                        }
                                    }
                                }
                    
                                // 缓存结果
                                配置.节点缓存.set(节点ID, 相关节点);
                                return 相关节点;
                            }
                
                            return null;
                        } catch (错误) {
                            return null;
                        }
                    })
                );
    
                // 合并到下一层
                for (const 结果 of 批处理结果) {
                    if (结果 && Array.isArray(结果)) {
                        结果.forEach(id => 下一层节点集.add(id));
                    }
                }
    
                // 批次间可选延迟
                if (配置.请求间隔 > 0 && i + 配置.并发请求数 < 本层节点.length) {
                    await new Promise(resolve => setTimeout(resolve, 配置.请求间隔));
                }
            }
  
            // 准备下一层
            当前层节点 = Array.from(下一层节点集);
            当前深度++;
        }
    }
  
    // 显示已启用通知
    console.log(`[图谱深度增强] 已启用${当前模式}模式: 深度${配置.最大深度}, 并发${配置.并发请求数}, 节点上限${配置.最大处理总数}`);
  
    // 添加样式
    const 样式 = document.createElement('style');
    样式.textContent = `
        .graph-depth-notification {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background-color: rgba(0, 0, 0, 0.75);
            color: white;
            padding: 12px 16px;
            border-radius: 6px;
            z-index: 9999;
            font-size: 14px;
            transition: opacity 0.5s;
            opacity: 1;
        }
        .graph-depth-notification.fade {
            opacity: 0;
        }
    `;
    document.head.appendChild(样式);
  
    // 创建通知
    const 通知 = document.createElement('div');
    通知.className = 'graph-depth-notification';
    通知.textContent = `思源笔记关系图谱深度增强已启用 (${当前模式}模式)`;
    document.body.appendChild(通知);
  
    setTimeout(() => {
        通知.classList.add('fade');
        setTimeout(() => 通知.remove(), 500);
    }, 3000);
})(); 
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    25556 引用 • 105698 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    173 引用 • 1171 回帖 • 1 关注
1 操作
ACai 在 2025-05-08 01:17:59 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • ACai 1 1 赞同

    还是编一下吧 😋 😋 😋

    写这个片段的原因

    什么是深度?

    深度就是当 A链接到B链接到C链接到D

    • 打开 A 文档后我就可以在关系图谱中看到 从A到D 的整个关系路径
    • 看到 A、B、C、D 所关联的其他节点
    • 以及其他节点之间的联系

    俺寻思的思源关系图现状

    思源笔记的关系图处在一个比较尴尬的位置

    • 全局关系图只适合以可视化的方式查看自己的知识库有多壮观
      • 如果作为内容地图去使用,存在大量无关节点
    • 本地关系图显示节点过少(仅支持渲染当前节点的正反链节点)
      • 用户在使用本地关系图时就像开了厚重的战争迷雾一样,
      • 实用价值不如反链面板和块引计数。换句话说,我们在编辑一篇笔记时,看不到这篇笔记的正反链与其他笔记之间的联系

    这个片段可以用来干什么

    在本地图谱有深度的情况下,作为在文档树、搜索等传统方式之外

    • 用于快速跳转的路径地图。
    • 打开一个文档,就可以通过关系图谱快速记起相关联项目的结构
    2 回复
  • wilsons 1 评论

    6 啊,突发奇想,把关键词和关键函数封装下,可以搞个中文 js。如果改动太大就需要自己写解析器。😄

    另外,还是解释下好的不然别人无法知道它的作用。

    可惜这种纯前端的方法在性能方面还是差了些意思
    ACai 1 赞同
  • PearlLin

    上次见到菜哥的这句话:“如果关系图带有深度,那么作为漫游导航的路径会非常有用。”,看起来菜哥已经实现了。蹲蹲菜哥这里的“深度”具体是指?

    1 回复
  • 其实就是打开某一个文档时,可以看到更深层次的关系,而不仅限于当前文档的正反链。

    如果你以 moc 等数种方式手动构建了联系,应该能够明白。允许用户在强相关的块之间随意漫游,而不是通过文档树、搜索或是通过反链等方式一级一级跳转。

  • 对 日志类节点就卡死了, 刷新按钮一直转啥都不显示. 另外这个深度跟 插件:关系增强图 比起来,似乎还需改进.

    不过能把系统自带的残废拯救一下,已经相当不错.

    js 修改的关系图 3 个关系 ( 系统的图无法显示关系)

    image.png

    插件关系增强, 可以显示 10 个节点的关系, 还有个更深度的 有几十个

    image.png

    image.png

  • 顶 顶 顶 顶 顶 顶!

  • Imuvux

    支持,深度图谱的完整引用链路可以弥补思源所缺少的层级引用继承功能,只引用距离最近的块也能将关联传递得更远了。

请输入回帖内容 ...