V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
journey0ad
V2EX  ›  分享创造

在 NAS 上部署一个属于个人的磁力搜索引擎

  •  2
     
  •   journey0ad ·
    journey-ad · 53 天前 · 2880 次点击
    这是一个创建于 53 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前段时间想给 NAS 找点事情做,发现了 Bitmagnet 这个项目,用了几天发现爬虫的部分还不错,但是自带的 webui 有点简陋,有权限敞开、种子数量上来后搜索慢等问题,用起来总是不那么爽

    于是花了几天时间用 Nextjs + NextUI 写了个前端界面,顺带也是为了学一下 Next 的开发,界面功能参考了常见的一些磁力搜索引擎,顺带做了夜间模式、多语言、内容预览和搜索分词等功能

    详细的部署过程可以看仓库,提供有 Docker Compose 配置可以快速部署,也可以点 Demo 链接体验,Demo 为了避免版权等问题,内容是固定的

    仓库地址: https://github.com/journey-ad/Bitmagnet-Next-Web
    Demo: https://bitmagnet-next-web.vercel.app/

    预览:

    下面是一些开发过程的记录:

    原版的 Bitmagnet 自带一个 GraphQL 的 api 可以进行搜索,但用下来和 webui 遇到的问题是一样的,数量上去之后普遍搜索时间在几十秒,最重要的是返回的结果数量不准,没办法做分页

    关于搜索和索引问题我问过 bitmagnet 的开发者,他回复是已经结合种子标题和文件内容,在 torrent_contents.tsv 里创建了向量索引。研究后发现是先转罗马音然后存的向量,这样好处是可以用 pg 原生支持的 tsquery 来查询,而且转成罗马音后对应中文的是拼音,可以做到错字也能搜到,坏处是只要同音字就能搜到,就算同音字很离谱也是一样

    另外就是 bitmagnet 用了 go-unidecode 这个库做罗马音化,但这个库在 node 上没有一比一对应的,转罗马音的过程和 bitmagnet 做不到完全一致,影响搜索效果

    综合以上问题,决定自己写后端代码直连 DB 来查询,但之前没怎么搞过后端,更没搞过搜索这种东西,总之边写边测边改,搞出来了 gin 索引+传统 like 模糊匹配+分词,并根据每个关键词的词性确定为必须或非必须,生成对应的 SQL 查询这种野路子方案。至于为什么不上 ES ,还有个考虑是想侵入性的修改尽量少点,后面 bitmagnet 库表结构有升级时好适配,所以没选 ES 这种重的方案

    结果比较多的情况下基本在几百毫秒到几秒内就能返回,部分收录少的词可能要跑全表,要等十几秒,这个性能还算能接受,暂时是我能想到的最好的方案了,等有空了研究一下 discuz 之类的论坛怎么做的搜索

    10 条回复    2024-07-22 18:59:23 +08:00
    chunkingName
        1
    chunkingName  
       53 天前
    是的,我目前 nas 上爬了三百多万种子,自带的搜索很慢,前几天连续搜索出错了,现在一直报错不进行搜索了,说数据库处于查询状态。试试你这个
    pxiphx891
        2
    pxiphx891  
       53 天前
    点赞,这个好
    chunkingName
        3
    chunkingName  
       53 天前
    搜索报这个错,用他原来的 web 搜索正常

    发生意外错误
    Message: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.

    Digest: 1841269720
    journey0ad
        4
    journey0ad  
    OP
       53 天前
    @chunkingName #3
    看下容器内的日志?
    chunkingName
        5
    chunkingName  
       53 天前
    @journey0ad date stream content
    2024/07/16 14:44:54 stderr }
    2024/07/16 14:44:54 stderr digest: '1841269720'
    2024/07/16 14:44:54 stderr at async y (/app/.next/server/app/search/page.js:1:11391) {
    2024/07/16 14:44:54 stderr at async b (/app/.next/server/app/search/page.js:1:10813)
    2024/07/16 14:44:54 stderr at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    2024/07/16 14:44:54 stderr at a (/app/.next/server/chunks/200.js:1:25217)
    2024/07/16 14:44:54 stderr Error: Network response was not ok: Internal Server Error
    2024/07/16 14:44:54 stderr at async y (/app/.next/server/app/search/page.js:1:11391)
    2024/07/16 14:44:54 stderr at async b (/app/.next/server/app/search/page.js:1:10813)
    2024/07/16 14:44:54 stderr at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    2024/07/16 14:44:54 stderr at a (/app/.next/server/chunks/200.js:1:25217)
    2024/07/16 14:44:54 stderr Error: Network response was not ok: Internal Server Error
    2024/07/16 14:44:54 stderr Failed to fetch: Network response was not ok: Internal Server Error
    2024/07/16 14:44:54 stdout keywords: [ { _: '$1', keyword: '雷米', required: true } ]
    2024/07/16 14:44:54 stderr }
    2024/07/16 14:44:54 stderr extraInfo: undefined
    2024/07/16 14:44:54 stderr networkError: null,
    2024/07/16 14:44:54 stderr clientErrors: [],
    2024/07/16 14:44:54 stderr protocolErrors: [],
    2024/07/16 14:44:54 stderr ],
    2024/07/16 14:44:54 stderr }
    2024/07/16 14:44:54 stderr extensions: [Object]
    2024/07/16 14:44:54 stderr path: [Array],
    2024/07/16 14:44:54 stderr locations: [Array],
    2024/07/16 14:44:54 stderr message: 'Failed to execute search query',
    2024/07/16 14:44:54 stderr {
    2024/07/16 14:44:54 stderr graphQLErrors: [
    2024/07/16 14:44:54 stderr at t.next (/app/.next/server/chunks/38.js:1:134768) {
    2024/07/16 14:44:54 stderr at b (/app/.next/server/chunks/38.js:1:134267)
    2024/07/16 14:44:54 stderr at g (/app/.next/server/chunks/38.js:1:133726)
    2024/07/16 14:44:54 stderr at Object.next (/app/.next/server/chunks/38.js:1:39726)
    2024/07/16 14:44:54 stderr at Object.then (/app/.next/server/chunks/38.js:1:39598)
    2024/07/16 14:44:54 stderr at new Promise (<anonymous>)
    2024/07/16 14:44:54 stderr at /app/.next/server/chunks/38.js:1:39631
    2024/07/16 14:44:54 stderr at o (/app/.next/server/chunks/38.js:1:39716)
    2024/07/16 14:44:54 stderr at /app/.next/server/chunks/38.js:1:76524
    2024/07/16 14:44:54 stderr at new t (/app/.next/server/chunks/38.js:1:88954)
    2024/07/16 14:44:54 stderr t [ApolloError]: Failed to execute search query
    2024/07/16 14:44:54 stderr }
    2024/07/16 14:44:54 stderr port: 5433
    2024/07/16 14:44:54 stderr address: '192.168.11.2',
    2024/07/16 14:44:54 stderr syscall: 'connect',
    2024/07/16 14:44:54 stderr code: 'ECONNREFUSED',
    2024/07/16 14:44:54 stderr errno: -111,
    2024/07/16 14:44:54 stderr at async Object.g [as search] (/app/.next/server/app/api/graphql/route.js:131:107) {
    2024/07/16 14:44:54 stderr at async Promise.all (index 1)
    2024/07/16 14:44:54 stderr at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    2024/07/16 14:44:54 stderr at /app/node_modules/pg-pool/index.js:45:11
    2024/07/16 14:44:54 stderr Error in search resolver: Error: connect ECONNREFUSED 192.168.11.2:5433
    2024/07/16 14:44:54 stdout [ '%雷米%', 10, 0 ]
    2024/07/16 14:44:54 stdout filtered; -- 从过滤后的数据中查询
    2024/07/16 14:44:54 stdout FROM
    2024/07/16 14:44:54 stdout END AS files -- 结果别名设为 'files'
    2024/07/16 14:44:54 stdout ELSE NULL -- 如果 files_count 为空, 则设置为 NULL
    2024/07/16 14:44:54 stdout )
    2024/07/16 14:44:54 stdout WHERE torrent_files.info_hash = filtered.info_hash -- 根据 info_hash 匹配文件
    2024/07/16 14:44:54 stdout FROM torrent_files
    2024/07/16 14:44:54 stdout ))
    2024/07/16 14:44:54 stdout 'extension', torrent_files.extension -- 文件扩展名
    2024/07/16 14:44:54 stdout 'size', torrent_files.size, -- 文件大小
    2024/07/16 14:44:54 stdout 'path', torrent_files.path, -- 文件在种子中的路径
    2024/07/16 14:44:54 stdout 'index', torrent_files.index, -- 文件在种子中的索引
    2024/07/16 14:44:54 stdout SELECT json_agg(json_build_object(
    2024/07/16 14:44:54 stdout -- 如果有数量, 根据 info_hash 查询文件信息到 'files' 列, 聚合成 JSON
    2024/07/16 14:44:54 stdout WHEN filtered.files_count IS NOT NULL THEN (
    2024/07/16 14:44:54 stdout CASE
    2024/07/16 14:44:54 stdout -- 检查 files_count, 是否有文件数量
    2024/07/16 14:44:54 stdout filtered.files_count, -- 种子文件数
    2024/07/16 14:44:54 stdout filtered.updated_at, -- 更新时间戳
    2024/07/16 14:44:54 stdout filtered.created_at, -- 创建时间戳
    2024/07/16 14:44:54 stdout filtered.size, -- 种子大小
    2024/07/16 14:44:54 stdout filtered.name, -- 种子名称
    2024/07/16 14:44:54 stdout filtered.info_hash, -- 种子哈希
    2024/07/16 14:44:54 stdout SELECT
    2024/07/16 14:44:54 stdout -- 从过滤后的数据中查询文件信息
    2024/07/16 14:44:54 stdout )
    chunkingName
        6
    chunkingName  
       53 天前
    @journey0ad 我将默认数据库 5432 改为了 5433 其余的没动 原版网页搜索正常,你的这个报日志这些错误
    journey0ad
        7
    journey0ad  
    OP
       53 天前
    @chunkingName #6
    看上去是 webui 到数据库容器的连接不通

    是用 docker compose 部署的吗? docker 内有网络隔离,db 连接串的 host 部分需要和 postgres 所在的容器名一致才能连接上,参考 https://github.com/journey-ad/Bitmagnet-Next-Web/blob/1981a093cea8291e476fadab82bdf4b07bc207a4/docker-compose.yml
    43 行的容器名和暴露的端口号,和 11 行 db 连接串的 postgres:5432 是对应的,29 行的 POSTGRES_HOST=postgres 也是一样的道理

    或者不管用什么方法,保证 webui 的容器到 postgres 容器是可联通的就行
    lisawang
        8
    lisawang  
       50 天前
    我在宝塔里用直接复制的 docker-compose.yml 用 Docker Compose 部署,ip:3000 搜索就是错误,但是:3333 是没问题,进去 ping 了一下 PostgreSQL ,ping 的通,奇怪,怎么解决呢
    发生意外错误
    Message: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.

    Digest: 2644143229
    lisawang
        9
    lisawang  
       50 天前
    TypeError: fetch failed
    at node:internal/deps/undici/undici:12618:11
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async a (/app/.next/server/chunks/200.js:1:25185)
    at async b (/app/.next/server/app/search/page.js:1:10813)
    at async y (/app/.next/server/app/search/page.js:1:11391) {
    cause: Error: connect ECONNREFUSED ::1:3000
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)
    at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:128:17) {
    errno: -111,
    code: 'ECONNREFUSED',
    syscall: 'connect',
    address: '::1',
    port: 3000
    },
    digest: '2644143229'
    }
    错误日志
    journey0ad
        10
    journey0ad  
    OP
       47 天前
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1811 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 00:34 · PVG 08:34 · LAX 17:34 · JFK 20:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.