[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); })();
  • 思源笔记

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

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

    25876 引用 • 107178 回帖
  • 代码片段

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

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

    177 引用 • 1254 回帖
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

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

请输入回帖内容 ...