Hugo博客文章过长导致search文件无法上传到algolia的问题

问题

我hugo使用algolia进行搜索,在构建hugo生成静态文件后,将search.json传到algolia中,就可以进行搜索。现在出现了一个小问题,就是我有一篇文章比较长,导致在上传时超过algolia免费套餐的限制,报错如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/opt/1panel/www/sites/cbba-top/index/blog-FixIt/node_modules/atomic-algolia/lib/update.js:69
          if (err) throw err;
                   ^
AlgoliaSearchError: Record at the position 1 objectID=/posts/555833d/:0:0 is too big size=10236/10000 bytes. Please have a look at https://www.algolia.com/doc/guides/sending-and-managing-data/prepare-your-data/in-depth/index-and-records-size-and-usage-limitations/#record-size-limits
    at success (/opt/1panel/www/sites/cbba-top/index/blog-FixIt/node_modules/algoliasearch/src/AlgoliaSearchCore.js:377:32)
    at process._tickCallback (internal/process/next_tick.js:68:7)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! blog-fixit@1.0.0 algolia: `atomic-algolia`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the blog-fixit@1.0.0 algolia script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2025-07-27T14_49_03_656Z-debug.log

解决思路有两个:

  1. 生成静态文件后,编写一个脚本将search.json中超出10k的文章拆分成多个algolia记录
  2. 生成静态文件后,编写一个脚本将

于是我测试了一下hugo使用algolia在网页中搜索好不好用,测试下来发现还行,但是涉及到某个细微的点就容易搜索不到,比如我哪个文章中提及了点别的技术,我想不起来是哪篇文章就只能靠搜索,这时网页大概率是搜不出来的,还是要靠本地搜索,所以我选择在生成search.json后将文章过长的部分截断

通过truncate.js截断search.json

以下是针对Algolia记录大小限制的截断代码truncate.js,实现按字节数检测超长内容并截断

hugo主目录创建truncate.js文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const fs = require('fs');
const path = require('path');

const INPUT_FILE = 'search.json';
const OUTPUT_FILE = 'search-truncated.json';
const MAX_BYTES = 9000; // 10KB,留出余量

// UTF-8 字节计算函数
function getByteLength(str) {
  return Buffer.byteLength(str, 'utf8');
}

// 截断内容,使其不超过 MAX_BYTES 字节
function truncateToBytes(str, maxBytes) {
  let bytes = 0;
  let result = '';
  // 通过for循环,逐个读取字符,每次都判断读取到现在位置的总字节数是否超过最大值,
  // 如果没超过则将该字符添加到result字符串中,直至总字节数超过最大值,此时的result字符串就是截断后保留的部分
  // 逐个字符处理可以避免在字符中间截断导致乱码
  for (let i = 0; i < str.length; i++) {
    const char = str[i];
    const charByteLength = Buffer.byteLength(char, 'utf8');
    if (bytes + charByteLength > maxBytes) {
      break;
    }
    bytes += charByteLength;
    result += char;
  }

  return result;
}

try {
  const rawData = fs.readFileSync(INPUT_FILE, 'utf8');
  const records = JSON.parse(rawData); // 解析json数据

  console.log(`✅ 成功加载 ${records.length} 条记录`);

  const processed = records.map((item, index) => {
    const contentBytes = getByteLength(item.content || ''); // 计算内容字节数
    const title = item.title || `第${index + 1}条记录(无标题)`; // 处理空标题

    // 如果字节数超过限制则截断
    if (contentBytes > MAX_BYTES) {
      const truncated = truncateToBytes(item.content, MAX_BYTES);
      console.log(`✂️ 截断处理,标题: ${title},原字节数: ${contentBytes} -> 截断后: ${getByteLength(truncated)} 字节`);
      return { ...item, content: truncated }; // 返回截断后的新对象
    } else {
      console.log(`👍 无需截断,标题: ${title},字节数: ${contentBytes}`);
      return item; // 返回原对象
    }
  });

  fs.writeFileSync(OUTPUT_FILE, JSON.stringify(processed, null, 2), 'utf8');
  console.log(`✅ 写入成功: ${OUTPUT_FILE}`);
} catch (err) {
  console.error(`❌ 错误: ${err.message}`);
}

修改deploy.sh文件

  • 将上传的文件由public/search.json改为主目录中新生成的search-truncated.json文件
  • 添加执行截断脚本的指令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash

# 构建 Hugo 网站
hugo

# 对超过algolia要求的大小的记录进行截断
node truncate.js

# 上传到 Algolia
ALGOLIA_APP_ID=ZSH625MG3A \
ALGOLIA_ADMIN_KEY=a4221316a50d7dea962f44d3593eae86 \
ALGOLIA_INDEX_NAME=index.zh-cn \
ALGOLIA_INDEX_FILE=search-truncated.json \
npm run algolia
0%