批量导入文档到数据库 0.0.6 版全新发布

前言

这个脚本的作用就是批量导入文档或块到数据库,包括后代文档等,可以通过限定嵌套层数或通过 SQL 等对导入内容加以限制。

在之前的这个帖子中(该贴不再维护和更新),我实现了 0.0.5 版本,这个版本已经很完善了,只不过使用需通过代码配置,体验上差了些。0.0.6 在这个基础上做了功能扩展,改善了使用体验。

使用效果,先上图

r41.gif

完整代码

使用方法

基本使用

  1. 下载这个文件批量导入文档到数据库.md.zip,放到/data/templates 目录
  2. 然后在使用的文档中输入斜杠命令 / 》模板 》选择“批量导入文档到数据库”模板即可
  3. 或者复制上面的代码,然后输入/{{}},在弹出的对话框中输入上面的代码即可
  4. 事实上就是 SQL 查询脚本,可以在任何文档中使用,按照脚本提示或注释说明使用即可

这个脚本非常灵活,可以调整不同的参数满足不同的需求。

但通常有四大使用场景,这里重点介绍下。

四大使用场景

一、临时使用

需要要修改参数:

let docBlockId = '';

let dbBlockId = '';

const isShowInputBox = true;

注意,docBlockId 和 dbBlockId 至少一个内容保持为空即可

然后可以保存为模板,然后每次使用时从模板导入即可。

使用演示:

r43.gif

二、自动导入

这个功能,可以满足当打开某个文档或刷新文档时,自动导入某个文档下面的文档到数据库。

需要修改参数:

let docBlockId = 'xxxxxxxxx-xxxx';

let dbBlockId = 'xxxxxxxxx-xxxx';

const runOnLoad = true;

注意,docBlockId 和 dbBlockId 不能为空,runOnLoad 需要设置为 true

使用演示:

r44.gif

三、手动执行

这个没有特别要求,任何参数配置情况下点击右侧的刷新按钮都可以手动执行。

使用演示:

r45.gif

四,自定义导入

这个就要参考代码中的参数配置和注释并根据自身情况进行设置了,脚本的本质就是根据 SQL 查询语句的内容进行导入的,在这里你可以通过编辑 SQL 进行自定义查询,然后导入。

image.png

使用技巧

你可以根据自身的需要对参数或代码进行修改,然后修改完成后,通过到导出到模板按钮保存为不同的模板,以满足不同场景下的需求和复用。

image.png

另外,可以在导出模板时修改 shortName 参数和 memo,让不同模板显示不同的名称和备注信息,这样就能很好区分,当前使用的脚本是哪个模板了。

免责声明

文件读写有风险!导入前请做好备份!请在新空间测试无误后再使用!本脚本仅用于学习交流,造成的任何后果均与脚本及作者无关。

  • 思源笔记

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

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

    22249 引用 • 88922 回帖
  • 脚本
    20 引用 • 147 回帖 • 1 关注
  • SQL
    126 引用 • 381 回帖 • 3 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    339 引用 • 708 回帖

相关帖子

优质回帖
  • 5kyfkr 1

    是漏子文档下的一个文档,父文档 A,底下有 A1 A2 A3,然后会漏一个 A3,并且添加的时候预览里面会多个“未命名”文档,实际点确认后不会被添加到数据库

  • wilsons 1

    我就是把这个注释去了然后出现上面的问题 ,问了 AI 感觉需要改的挺多,怕改坏了

    @5kyfkr

    已修复,那个未命名文件是 bug 多出的,这个是因为 SQL 排除了父文档后,在排序的过程中没有排除父文档导致父文档重新出现,现已修复,你重新复制上面的代码再试试。

    同时修复了层级限制的 bug,漏导入可能是这个 bug 引起的。

  • 5kyfkr 1

    大佬您好,我又发现个问题了,在第二层文档设置为父文档的时候检索不到第三层文档,如下图,设置父文档是 2 的时候找不到文档 3 和他自己,只有设置为父文档 1 的时候能找到文档 2 和文档 3

    image.png

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • 非常好 支持 加油

  • 顶,大佬太强了

  • mark-j

    慎用自动导入,会内核崩溃.

    1 回复
  • wilsons

    什么意思?能否详细说明下你的问题?

  • mark-j

    const runOnLoad = true;开启这个选项,内核会崩溃,总共也才 30 个文档。

    2 回复
  • wilsons

    我并没有测试到你说的问题,可能你的环境和某种脚本有冲突,因为这涉及到编辑器内容的更新和变化,比如,如果环境有脚本监听编辑器的某些变化做某种操作,但这种操作又引起编辑器的变化,这可能导致无限循环。

    建议在新空间测试试试。

    如果还有问题,建议把你的环境打包发过来我测试下,比如把 data 目录去敏后打包。

  • wilsons

    建议在代码中增加 console.log('111111')(比如,配置区上面或主逻辑区上面),然后在 devtools 控制台观察刷新时是否一直在打印,如果一直打印就说明本脚本被某种原因死循环了。如果没有,则说明本脚本没问题,可能是别的脚本导致的。

    如果确实本脚本由于某种原因出现死循环了,可以添加以下代码临时解决(把以下代码放到配置区的上面或主逻辑区的上面均可)。

    if(window.doc2dbImporterRuning) return render(`<span>已执行完毕,不必重复执行</span>${help()}`);
    window.doc2dbImporterRuning = true;
    // 这里保证5秒钟内只会被执行一次,可根据您脚本具体执行时间进行调整(这种调整通常可停止某些原因导致的死循环)
    setTimeout('window.doc2dbImporterRuning = false', 5000);
    

    如果是别的脚本的问题,那就要具体排除是哪个脚本及具体的原因了。

    1 回复
  • mark-j

    我去本地笔记文件内把自动去掉了,还是手动吧。

    1 回复
  • wilsons

    嗯,怎么使用,你自己决定。我只是提供一种排查方式,如果暂时没有这个需求,那就用到时再说吧。

  • 大佬,那个不包含父文档本身的功能开了会漏一个文章没导入进数据里

    1 回复
  • wilsons

    漏哪个文档?这个功能的含义是不包含父文档自身,回忽略父文档的导入。比如,你的父文档是 A,只会导入 A 文档下的后代文档,而不包含文档 A。

    你说的漏文档是指漏哪个文档?是漏文档 A 吗?还是?

    1 回复
  • 5kyfkr 1

    是漏子文档下的一个文档,父文档 A,底下有 A1 A2 A3,然后会漏一个 A3,并且添加的时候预览里面会多个“未命名”文档,实际点确认后不会被添加到数据库

    1 回复
  • wilsons 1 评论

    这是你本地环境问题引起的特例,并不是通用问题,我这里无法重现。

    能否把你的测试文件和脚本去敏后打包上传?

    我这边测试如下

    r55.gif

    你这图里没开排除父文档吧,A 都被添加进去了,晚点我排除影响试试
    5kyfkr
  • wilsons 1 评论

    你这图里没开排除父文档吧,A 都被添加进去了,晚点我排除影响试试

    @5kyfkr 默认是不排除父文档的,如果需要排除的话,必须在脚本中去掉下面箭头处的这个注释。(评论里不能发图,只能回复了)

    image.png

    这个脚本本质就是把查询结果导入,真正起作用的是这个 SQL,图形界面只不过是辅助生成 SQL,最终执行的还是这个 SQL。

    我就是把这个注释去了然后出现上面的问题 ,问了 AI 感觉需要改的挺多,怕改坏了
    5kyfkr
  • wilsons 1

    我就是把这个注释去了然后出现上面的问题 ,问了 AI 感觉需要改的挺多,怕改坏了

    @5kyfkr

    已修复,那个未命名文件是 bug 多出的,这个是因为 SQL 排除了父文档后,在排序的过程中没有排除父文档导致父文档重新出现,现已修复,你重新复制上面的代码再试试。

    同时修复了层级限制的 bug,漏导入可能是这个 bug 引起的。

    1 回复
    1 操作
    wilsons 在 2024-09-16 21:29:31 更新了该回帖
  • 感谢大佬,已经好了 👍

  • fdtl01

    感谢大佬

  • 大佬,我想把日记本中的每日日记,比如 2024-09-18 这个文档,自动发送到某个数据库,应该如何操作啊?

    这里的难处在于,日记的路径每月都会变化,比如 2024-10-01 这个文档就会在 Daily/2024/10 路径下。

    感谢!

    image.png

    1 回复
  • wilsons

    假设你的数据库块在 demo 文档中

    第一步,在 demo 文件中新建 sql 查询,复制上面的代码并粘贴到 SQL 查询对话框里,然后修改以下参数

    // 定义父文档块id,可选,根据自己的父文档块id进行修改
    let docBlockId = '20240713143249-k0g5vnz';
    
    // 定义数据库块id,必填,根据自己的数据库块id进行修改
    let dbBlockId = '20240918091455-da65cmn';
    
    // 设置最大嵌套层级,默认7级,这个是从笔记下的一级目录开始算1级
    let maxLevel = 4;
    
    //获取所有子文档的sql查询语句
    const docsSql = (docBlockId, maxLevel, extendSql) => `
        select *
        from blocks
        where type = 'd'
        -- 查询所有子文档
        and path like '/${docBlockId}%'
        -- 不包含父文档自身
        and id != '${docBlockId}'
        -- 动态扩展SQL的变量
        ${extendSql}
        -- 查询文档层级限制最大多少级
        and (LENGTH(path) - LENGTH(REPLACE(path, '/', ''))) / LENGTH('/') = ${maxLevel}
        order by sort asc, created desc;
    `;
    
    // 加载时是否执行,默认false,加载时不执行,true为加载时执行
    const runOnLoad = true;
    
    // 排序方式, 默认为true,按文档树顺序排序后插入,false 按SQL搜索结果插入
    const sortByTreeOrder = false;
    

    说明:

    1. 这里主要操作是 maxLevel 设置为 4,然后 docsSql 里修改 <=${maxLevel}= ${maxLevel},这样就仅导入第 4 级的文档
      这里的层级计算如图所示

      image.png

    2. 去掉 docsSql 里的注释 -- and id != '${docBlockId}',以排除父文档的导入

    3. 开启 runLoad=true,让刷新时执行

    4. 关闭 sortByTreeOrder=false,排除子文档排序带来的意外影响(这个后续版本会修复,不关闭也不影响)

    第二步,然后只要每次打开 demo 这个文档的时候就会自动导入了,当然也可以通过手动点右侧刷新导入。

    总结:总之这个查询的关键还是对 SQL 查询的运用,SQL 查询的结果便能导入。

    2 回复
  • 大佬搞崩溃了 😂
    66JGAJ3WZA1QIJ4R6.png

  • 原因是在按照你评论写的,修改数字的过程中出的问题,我把对应的文档删掉就没事了

    2 回复
  • wilsons

    没太懂,什么叫修改数字过程中?修改什么数字?对应文档又指什么?

  • wilsons

    你这里的数字是指修改脚本参数吗?意思是参数修改到一半的时候,不小心脚本被执行了吗?

    如果是这样的话,可能是由于参数问题导致了死循环。这种问题,可参考 批量导入文档到数据库 0.0.6 版全新发布 - wilsons 的回帖 这个回帖临时解决,后续会加入防死循环机制。

    1 回复
  • 对对对就是这个

  • lkqfys

    请问下,为啥我复制完父文档 ID 和数据库 ID,层级选 2,结果提示 0 文档,实际上对应的父文档下面是有很多子文档的。

    1 回复
  • wilsons

    有没有可能是因为你的层级 2 选的不对?可参考下图来看看是否选的正确,红色数字代表层级,比如你选 2,则代表只能导入 daliy note 根目录的文档和 2024 根目录的文档,而 09 和 08 下的文档不会导入。

    通常情况下,层级不用选的,比如你设置一个非常大的数字,通常意味这这个文档下的所有后代文档都会导入,这个层级限制主要用来,你只想导入某个层级及之前的文档,而不是从这个层级开始往后。

    image.png

    如果还有问题,不妨说下你的导入需求,并把你的层级截图看看。

    1 回复
  • lkqfys

    1727234543837.jpg1727234551713.jpg

    复制 ID 没错,层级也确认正常,就是导入的时候无法识别出来,不知道哪里出了问题?楼主帮忙看看~

    2 回复
  • wilsons

    图片看不出来,请把你的有问题的数据去敏后打包发到论坛。然后告知导入的是哪个目录?数据库在哪个文件里。

    1 回复
  • lkqfys

    我测试了一下,新建一个新的父文档,并在其下建立子文档,这个模板能正常运行的。

    所以是我之前的文档出了问题,不知道为啥检索不到父文档下面的子文档。。。但是用另一个列出子文档挂件,是能正常检索到下面的子文档的。

    1 回复
  • wilsons

    这个不是常规问题,是特例,你必须把相关数据给我我才能去查看原因,或者你可以尝试,比如把 sortByTreeOrder 设为 false, maxLevel 设为较大值试试,或者把 SQL 复制出来查询试试等。

    总之,非开发人员,光靠描述很难看出问题(因为很难准确描述到问题症结),除非你能说到关键点。

    1 回复
  • 5kyfkr 1

    大佬您好,我又发现个问题了,在第二层文档设置为父文档的时候检索不到第三层文档,如下图,设置父文档是 2 的时候找不到文档 3 和他自己,只有设置为父文档 1 的时候能找到文档 2 和文档 3

    image.png

    1 回复
  • wilsons

    是的,3 是第 3 级了

    必须设置为 3 才行

    这个层级的原理是根据文档的路径来判断的

    比如通过如下菜单复制

    image.png

    然后,如果是 /1/2/3 路径,则包含 3 个 / 即为 3 级,如果 /1/2 ,则包含 2 个/ 则是 2 级,如果 /1 则为 1 级。

    1 回复
  • 是图里这个地方设置 3 吗,试了没效果

    image.png

    2 回复
  • wilsons

    这个弹窗的优先级最高,这里的设置会覆盖源码中的设置

    image.png

    如果,isShowInputBox=false,取消弹窗的情况下,源码里的 maxLevel 才生效。

  • wilsons

    感谢大佬的反馈!

    如果父文档块 ID 不是 1 级目录,需要把

    and path like '/${docBlockId}%'

    改为
    and path like '%${docBlockId}%'

    现已修复。

    @lkqfys 你看下你的问题是否这个问题引起的,用新代码测试下。

    1 回复
  • wilsons

    【提醒】

    思源默认最大查询返回结果限制为 64 条记录,如果您的子文档数大于 64,需在设置-> 搜索-> 搜索结果显示数里修改限制

    感谢 @5kyfkr 的提醒和反馈!

  • lkqfys

    恩,谢谢你~我也觉得这个是个人特例,另外想问下,如果把父文档 A 下的子文档 A1 移动到了父文档 B 下,数据库自动更新后并不会删除这个子文档 A1(因为 A1 还存在),这个有解决方法吗?

    1 回复
  • lkqfys

    大佬,代码更新果然把问题解决了,太感谢了!~🙏

  • wilsons

    导入时,导入的是 A 目录此刻的文档状态,如果已存在则忽略,不存在则导入,并不会删除数据库原有数据。

    且,思源并不会记录一个文档的历史所在文件夹,因此无法判断数据库中的某个文档,上一次导入时的文件夹是什么。所以,除非你这个数据库是和 A 文件夹一一对应的,且不包含 A 文件夹以外的任何文档,这种情况下,可以在导入时判断已存在的文档是否在 A 目录中,如果不存在则删除。

    另外,如果确实需要包含 A 目录以外的文件夹的文档,需要你自己记录上一次导入时的文档所在文件夹状态,然后再次导入时,根据这个记录去判断已存在文档的目录是否被改变。

    所以,有没有办法解决,怎样解决,看你的需求。但目前的脚本无法实现,需要改造,目前暂不考虑这种需求,如果你有这方面的需求可自行研究改进或请大佬帮忙解决。

  • mark-j

    新建文本文档.zip

    image.png想把电影标签的块,导入,但是错误!image.png

    1 回复
  • wilsons

    image.png

  • 找到思源笔记添加绑定块的 api 了,建议用这个,目前你的代码如果文档块删除后,还没更新索引,sql 能查到,但是这个 id 是不存在的,就会内核中断。用 api 没有这个问题

    数据库添加绑定块/api/av/addAttributeViewBlocks

    • avID:数据库 id,非数据库块 id,可在 DOM 中找
    • blockID:数据库块 id,非添加的块 id
    • srcs
      • id:块 id
      • isDetached
        • false:是绑定块
        • true:是非绑定块
    const docids = ['20240928004914-pwh0yxz']
    const srcs = docids.map(docId => ({
        "id": docId,
        "isDetached": false,
    }));
    avid = '20240928104630-r293qm7' // 数据库
    avblockID = '20240928111535-ts1vz4e' // 数据库块id
    
    
    const input = {
      "avID": avid,
      "blockID": avblockID,
      'srcs': srcs
      
    }
    const result =await fetchSyncPost('/api/av/addAttributeViewBlocks', input)  
    
    1 回复
  • wilsons

    我刚开始用的就是这个 api,这个 api 插入数据后,数据库不会刷下,需要手动刷新页面才行,你那边有这个问题吗?我空了再试试看。

    1 回复
  • 我测试没有这个问题,即时更新的

  • 比如 我有四级文档 我就想导入 4 级文档 或者屏蔽某个文档要怎么设置

    1 回复
  • wilsons

    SQL 添加

    and (LENGTH(path) - LENGTH(REPLACE(path, '/', ''))) / LENGTH('/') = 4
    and id not in ('要屏蔽的文档id1', '要屏蔽的文档id2')
    
  • White-Night-Dream 1 评论

    感谢大佬 脚本很有用 我这里还有个需求不知道能不能实现 我用
    select * from blocks
    where path like '%${docBlockId}%' and markdown like '%✨%' and (type ='p' or type='h' or type='d' or type='t') order by created asc,sort asc ;来整理一个文档的所有标记好的知识点用于复习 同时 我希望他们添加到数据库时能够读取他们的面包屑该如何实现 (面包屑不行,母一级标题也行 )我想到的可以用脚本先写入面包屑到属性里

    1 回复
    主要就是面包屑方便排序
    White-Night-Dream
  • wilsons 2 评论

    不支持导入数据库时自定义标题内容,思源也没提供相关 api。

    1 回复
    我记得层级导航插件可以读取块的面包屑,应该是有相关 API 能否用脚本将它写入到块中,作为一个自定义属性,然后用模板列读取就行。
    White-Night-Dream
    @White-Night-Dream 可以读取面包屑,但无法把面包屑内容作为标题导入数据库,即导入数据库时,思源 api 只提供了块 id 参数,导入数据库的标题无法自定义
    wilsons
  • White-Night-Dream 1 评论

    佬可能我没有表示清楚 我不需要面包屑做标题 只是拿来排序用的

    1 回复
    所以可以写入到属性,然后用模板列读取
    White-Night-Dream
  • wilsons

    抱歉,还是不太清楚你的具体需求,根据你的描述,把面包屑写到块属性中,然后在数据库模板列中读取块属性的值?

    假设有 A,B,C 三个段落块,导入到数据库 D 中,然后把 A,B,C 的面包屑分别写入到 A,B,C 三个块属性中,然后在数据库 D 中新建模板列 T,然后在 T 中读取 A,B,C 三个块属性中的面包屑的值(怎么读取?)?然后在通过模板列 T 对已导入数据库的块进行排序?

    3 回复
请输入回帖内容 ...