前缀文档树:智能文档管理新方案,新增 Tags 面板联动

前缀文档树功能说明

一、核心功能与价值

前缀文档树可自动聚合同名前缀文档,与传统文档树相比:
● 传统文档树:需人工维护层级结构
● 前缀文档树:仅需规范文档命名,自动收集同前缀文档
→ 特别适合管理系列文章(如《如何...》《高中物理...》等)

二、工作原理

1. 前缀匹配规则

● 打开文档时自动扫描工作空间内所有文件
● 筛选与当前文档前缀相同的文件(如打开《高中物理·力学》会显示所有“高中物理”开头的文档)
● 智能显示最长前缀匹配结果
● 以日记为例:

image.png

2. 竖线分级功能

● 用竖线“|”分割文档名创建虚拟层级(例:“物理 | 高中 | 力学”):
→ 系统独立匹配每个关键词段
→ 同系列文档(如“力学 | 物理 | 高中”)打开任意篇即可互相关联

● 分级特性说明(例:“物理 | 高中 | 力学例题集一”):
→ “物理”和“高中”自动加入 tags 面板
→ 最后段“力学例题集一”作为文档显示名

3. Tags 面板联动

● 集中显示所有前缀集合(每个前缀相当于类别标签)
● 点击标签可快速定位该类别中最新更新的文档
● 与前缀文档树协同实现双向管理:
→ 前缀文档树:按名称聚合内容
→ Tags 面板:提供跨类别快速切换

三、技术优势

● 即时响应:前缀匹配操作瞬间完成
● 零维护成本:文档物理存放位置不影响功能

四、最佳实践

配合快速笔记功能

  1. 通过快捷指令创建新文档
  2. 系统自动生成时间目录(例:f2025-06/xx 文档)

→ 双重管理优势:
● 前缀文档树:按内容主题聚合文档
● 时间目录:按创建时间追溯历史版本
● 彻底免除手动维护目录层级的负担

image.png

  • 思源笔记

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

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

    28446 引用 • 119789 回帖
3 操作
player 在 2025-07-09 16:28:28 更新了该帖
player 在 2025-07-09 16:27:42 更新了该帖
player 在 2025-06-27 19:01:16 更新了该帖

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • MasterYS 1 评论

    书签 + 应该也能实现类似功能吧

    1 回复
    不大清楚,应该侧重不同。
    player
  • 其他回帖
  • player

    新增 Tags 面板联动

  • wilsons 1 赞同 6 评论

    确实可以

    书签 + 实现

    async function main() {
        let content = await kit.sql(`select content from blocks where id='{{CurDocId}}'`);
        content = content[0]?.content;
        if (content) {
            // 竖线分级功能
            let keywords = content.split('|');
            keywords = keywords.map(word => `content like '%${word}%'`);
            // 前缀匹配规则
            let prefixArr = [];
            let str = content;
            while (str.length > 0) {
                prefixArr.push(str);
                str = str.slice(0, -1); // 每次去掉最后一个字符
            }
            prefixArr = prefixArr.map(word => `content like '${word}%'`);
            // 合并SQL
            keywords = [...keywords, ...prefixArr];
            const like = keywords.join(' or ');
            let result = await kit.sql(`select * from blocks where type='d' and id!='{{CurDocId}}' and (${like}) limit 500`);
            return result.map(block => block.id);
        }
    }
    return main();
    
    1 回复
    3 操作
    wilsons 在 2025-07-01 22:26:06 更新了该回帖
    wilsons 在 2025-07-01 22:17:25 更新了该回帖
    wilsons 在 2025-07-01 15:35:49 更新了该回帖
    强啊,W 佬
    MasterYS
    @MasterYS 之前仅支持竖线分级功能,现在也支持前缀匹配规则了。
    wilsons
    @wilsons 收到,只要把 let keywords = content.split('|'); 中的 | 替换掉就行了吧
    MasterYS
    @MasterYS 没懂你意思,我之前的版本,前缀匹配会删除 | 分割前的字符,新版去掉了
    wilsons
    @wilsons 我以为是你说,如果把代码里的 | 换成别的特殊符号或者文字,也可以作为分割。
    MasterYS
    @MasterYS 不是,楼主功能有 2 个,前缀匹配规则和竖线分级,现在也实现了 2 个功能,第一版仅支持一个。且我又实现了一个标题分词 +tag 模仿相似文章功能,见最新回复贴。
    wilsons
  • 前缀文档树,嵌入块方式实现

    [js] 告别 select * from blocks!嵌入块多字段查询来了

    相似文章(标题分词 +tag 实现)

    [js] 告别 select * from blocks!嵌入块多字段查询来了

    相似文章(书签 + 版)

    // 相似文章
    // api see https://api.yesapi.cn/docs-api-App.Scws.GetWords.html
    const appKey = '';
    // 是否使用标签筛选
    const useTag = true;
    async function main() {
        const doc = await kit.sql(`select content, tag from blocks where id='{{CurDocId}}'`);
        let content = doc[0]?.content;
        let tag = doc[0]?.tag;
        if (content) {
            // 获取分词
            const words = await requestApi('https://hn.api.yesapi.net', {s:'App.Scws.GetWords', return_data:0, yesapi_allow_origin: 1, text: content, app_key: appKey, sign:''});
            let keywords = words?.data?.words?.map(word => word?.word) || [];
            keywords = keywords.map(word => `content like '%${word}%'`);
            // 生成like sql
            const like = keywords.join(' or ');
            // 生成tag sql
            if(tag && useTag) {
                const tags = tag.split(/\s+/)?.filter(Boolean);
                const tagLike = tags.map(tag => `tag like '%${tag}%'`).join(' or ');
                tag = tags.length > 0 ? `or (${tagLike})` : '';
            }
            let result = await kit.sql(`select * from blocks where type='d' and id!='{{CurDocId}}' and ((${like}) ${tag||''}) order by content limit 500`);
            return result.map(block => block.id);
        }
    }
    async function requestApi(url, data, method = 'POST') {
        return await (await fetch(url, {method: method, body: JSON.stringify(data||{})})).json();
    }
    return main();
    
    1 回复
    1 操作
    wilsons 在 2025-07-02 19:47:28 更新了该回帖
  • 查看全部回帖