基于 Query&View 挂件实现的文档字数统计可视化功能(新增折线图、柱状图)
-1 更新情况
2025-05-08 星期四 更新:新增折线图、柱状图,效果如下。
0 写在前面
-
效果如图,文档名为可跳转链接,有单篇文档字数和汇总字数。
-
查询的文档可以 1.基于 sql 进行查询 2.自定义文档 id 进行查询 3.查询该文档下的一级子文档。
-
代码非常丑,不简洁不优雅(基于 deepseek 及破烂 js 基础的成品),有 js 基础的建议瞄两眼思路,自己重新写一版。
针对于文档查询,f 佬的文档计数封装了字数统计、链接统计、图片统计、ref(提及?)统计和块统计。
完整的接口文件请查看:https://github.com/frostime/sy-query-view/blob/main/public/types.d.ts
1 使用方法
- 思源笔记-设置-集市-插件-检索 query&view 插件(作者:frostime),下载安装并确保启用。
- 复制需要的代码,在思源笔记中的编辑器页面输入/触发斜杠菜单,选择“Query View 基础模板”,在右上方三个点点开代码编辑器,粘贴代码。
- 退出代码块编辑器页面,如页面没更新,点击代码块右上角的小圆圈刷新。
2 代码分享
-
基于 SQL 进行查询:
//!js const query = async () => { let dv = Query.DataView(protyle, item, top); // 1. 查询需要统计的文档(下方放入对应的sql) const SQL = ` select * from blocks order by random() limit 5;`; let docs = await Query.sql(SQL); // 2. 获取统计结果 const stats = await Promise.all(docs.map(async doc => ({ ...doc, // 展开运算符继承所有原始属性 runeCount: (await Query.docStat(doc.id)).stat.runeCount || 0 }))); // 3. 构建可视化 const total = stats.reduce((sum, cur) => sum + cur.runeCount, 0); const xdata = stats.map(item => String(item.content)); const ydata = stats.map(item => String(item.runeCount)); dv.addmd(`**文档统计汇总**(共 ${docs.length} 篇)`); dv.addmd(`**总字数**: ${total.toLocaleString()}`); dv.addebar(xdata, ydata, { //折线图(dv.addebar);柱状图(dv.addeline) title: '文档字数统计折线图', xlabel: '文档名', ylabel: '文档字数(Rune)' }); // 4. 单篇文档展示(标题 | 字数) //dv.addtable(docs, {fullwidth: false, cols: null}); //全部显示 dv.addtable(stats, {fullwidth: false, cols: ["root_id","runeCount"]}); //指定显示 dv.render(); } return query();
Q:思源的 SQL 是什么?能实现什么样的效果?
A:参考链接:思源 SQL 新人指南:SQL 语法 + Query + 模板 - 链滴(作者:Frostime)
Q:有没有更傻瓜式、开箱即用的做法?
A:请看这里:SQL 小助手 - 链滴(作者:Frostime)。把 f 佬写的这个 sql 编写 promt(页面内容)喂给 deepseek,把需求喂给 deepseek。
不过我依然建议你把上面那个新手指南看见几遍,理解其中的逻辑,以及查询的要点——可以不会,但至少知道 SQL 能做什么。然后明确自己的需求,用清晰、规范的语言写下自己希望 SQL 实现的查询内容,减少出错的可能。
温馨提示:
-
新建一个空白文档测试 sql 查询代码。
参考 3,卡死后重启思源,侧边栏直接删除文档即可。
-
SQL 小助手生成的是基于思源自生的 sql 语句,因此采用了换行符号。请在编辑器内粘贴 sql,再从代码编辑器中复制对应的语法。
-
不要用原生编辑器测试文档查询,将 select * 中的*删除再粘贴,然后在 query&view 中手动补回正确的查询语句。
思源对于文档块的渲染是全文档页面的渲染(类似于文档流),并且嵌入块是按块进行渲染的。(人话:思源很可能会卡死)
-
自定义文档 id 进行查询
//!js const query = async () => { let dv = Query.DataView(protyle, item, top); // ================== 配置区:手动输入 root_id ================== const targetRootIDs = [ "20250425091726-f1cj0zv", // 替换为实际 root_id "20250424090716-7mgtv3l",//英文逗号 "20250425091726-f1cj0zv", ]; // ========================================================== // 1. 获取 root_id 对应的文档基础信息 const SQL = `select * from blocks where type='d' and id in (${targetRootIDs.map(id => `'${id}'`).join(",")})`; const docs = await Query.sql(SQL); // 2. 获取统计结果 const stats = await Promise.all(docs.map(async doc => ({ ...doc, // 展开运算符继承所有原始属性 runeCount: (await Query.docStat(doc.id)).stat.runeCount || 0 }))); // 3. 构建可视化 const total = stats.reduce((sum, cur) => sum + cur.runeCount, 0); const xdata = stats.map(item => String(item.content)); const ydata = stats.map(item => String(item.runeCount)); dv.addmd(`**文档统计汇总**(共 ${docs.length} 篇)`); dv.addmd(`**总字数**: ${total.toLocaleString()}`); dv.addeline(xdata, ydata, { //折线图(dv.addeline);柱状图(dv.addebar) title: '文档字数统计折线图', xlabel: '文档名', ylabel: '文档字数(Rune)' }); dv.addebar(xdata, ydata, { //折线图(dv.addeline);柱状图(dv.addebar) title: '文档字数统计柱状图', xlabel: '文档名', ylabel: '文档字数(Rune)' }); // 4. 单篇文档展示(标题 | 字数) //dv.addtable(docs, {fullwidth: false, cols: null}); //全部显示 dv.addtable(stats, {fullwidth: false, cols: ["root_id","runeCount"]}); //指定显示 dv.render(); } return query();
-
查询该文档下的一级子文档
//!js const query = async () => { let dv = Query.DataView(protyle, item, top); // 1. 查询需要统计的文档 let docs = await Query.childdoc(dv.root_id); // 2. 获取统计结果 const stats = await Promise.all( docs.map(async doc => ({ ...doc, // 继承原文档属性 ...(await Query.docStat(doc.id)).stat || {}, // 合并统计接口返回的 stat 对象 })) ); // 3. 构建可视化 const total = stats.reduce((sum, cur) => sum + cur.runeCount, 0); const xdata = stats.map(item => String(item.content)); const ydata = stats.map(item => String(item.runeCount)); dv.addmd(`**文档统计汇总**(共 ${docs.length} 篇)`); dv.addmd(`**总字数**: ${total.toLocaleString()}`); dv.addeline(xdata, ydata, { //折线图(dv.addeline);柱状图(dv.addebar) title: '文档字数统计折线图', xlabel: '文档名', ylabel: '文档字数(Rune)' }); dv.addebar(xdata, ydata, { //折线图(dv.addeline);柱状图(dv.addebar) title: '文档字数统计柱状图', xlabel: '文档名', ylabel: '文档字数(Rune)' }); // 4. 单篇文档展示(标题 | 字数) //dv.addtable(docs, {fullwidth: false, cols: null}); //全部显示 dv.addtable(stats, {fullwidth: false, cols: ["root_id","runeCount"]}); //指定显示 dv.render(); } return query();
单独把这个查询提出来,是因为它用了 query&view 的封装,可以保存为模板快速插入,不用每次都输文档 id,也许有人会需要。
3 文档排序
- 在基于 sql 进行查询中,可以在 sql 中直接指定排序方式,如:文档名升序;创建时间升序;更新时间升序等。
- 在**// 3. 构建可视化**之前,可以针对 stats 的属性进行排序。
- 具体属性可以通过去掉代码中**//dv.addtable(docs, {fullwidth: false, cols: null}); //全部显示**最前面的// 查看。
4 最后
全篇都是 F 佬的痕迹。代表个人,给 F 佬赛博磕几个。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于