Do1e

Do1e

github
email

博客配置Algolia搜索,分頁以解除文本長度限制

此文由 Mix Space 同步更新至 xLog
為獲得最佳瀏覽體驗,建議訪問原始鏈接
https://www.do1e.cn/posts/code/algolia-search


Algolia 搜尋配置方法#

mx-space 的文檔中有比較詳細的配置教程,其他博客框架可能大同小異。

索引大小限制#

很不幸,在根據文檔配置完後,log 中報錯了:

16:40:40  ERROR   [AlgoliaSearch]  algolia 推送錯誤
16:40:40  ERROR   [Event]  Record at the position 10 objectID=xxxxxxxx is too big size=12097/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

出錯原因也很明確,有一篇博客太長了,而免費的 Algolia 每條數據僅有 10KB。對於我這種想白嫖的人怎麼能忍,馬上想辦法解決。

解決方案#

思路#

對於 mx-space 來說,可以配置 API Token 後從/api/v2/search/algolia/import-json獲取到手動提交到 Algolia 索引的 json 文件。
其中是一個包含了posts, pages 和notes的列表,示例數據如下:

{
  "title": "南京大學IPv4地址範圍",
  "text": "# 動機\n\n<details>\n<summary>動機來自於搭建的網頁。由於校內和公網都有搭建....",
  "slug": "nju-ipv4",
  "categoryId": "abcdefg",
  "category": {
  "_id": "abcdefg",
  "name": "其他",
  "slug": "others",
  "id": "abcdefg"
  },
  "id": "1234567",
  "objectID": "1234567",
  "type": "post"
},

其中objectID比較關鍵,提交給 Algolia 的必須唯一。

這裡我能想到的思路便是分頁,將有過長text的文章切分,同時修改objectID不就可以了?!(顯然,此時並沒有想到問題的嚴重性)
另外我的一些頁面裡會寫<style><script>,這部分也可以直接使用正則匹配刪掉。
於是有了如下 Python 代碼,編輯從上述接口下載的 json 並提交給 Algolia。

from algoliasearch.search.client import SearchClientSync
import requests
import json
import math
import os
from copy import deepcopy
import re

MAXSIZE = 9990
APPID = "..."
APPKey = "..."
MXSPACETOKEN = "..."
url = "https://www.do1e.cn/api/v2/search/algolia/import-json"
headers = {
  "Authorization": MXSPACETOKEN,
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0",
}

ret = requests.get(url, headers=headers)
ret = ret.json()
with open("data.json", "w", encoding="utf-8") as f:
  json.dump(ret, f, ensure_ascii=False, indent=2)
to_push = []

def json_length(item):
  content = json.dumps(item, ensure_ascii=False).encode("utf-8")
  return len(content)

def right_text(text):
  try:
    text.decode("utf-8")
    return True
  except:
    return False

def cut_json(item):
  length = json_length(item)
  text_length = len(item["text"].encode("utf-8"))
  # 計算切分份數
  n = math.ceil(text_length / (MAXSIZE - length + text_length))
  start = 0
  text_content = item["text"].encode("utf-8")
  for i in range(n):
    new_item = deepcopy(item)
    new_item["objectID"] = f"{item['objectID']}_{i}"
    end = start + text_length // n
    # 切分時要注意確保能被正確解碼(中文占2個字節)
    while not right_text(text_content[start:end]):
      end -= 1
    new_item["text"] = text_content[start:end].decode("utf-8")
    start = end
    to_push.append(new_item)

for item in ret:
  # 刪除style和script標籤
  item["text"] = re.sub(r"<style.*?>.*?</style>", "", item["text"], flags=re.DOTALL)
  item["text"] = re.sub(r"<script.*?>.*?</script>", "", item["text"], flags=re.DOTALL)
  if json_length(item) > MAXSIZE: # 超過限制,切分
    print(f"{item['title']} is too large, cut it")
    cut_json(item)
  else: # 沒超限制也修改objectID以保持一致性
    item["objectID"] = f"{item['objectID']}_0"
    to_push.append(item)

with open("topush.json", "w", encoding="utf-8") as f:
  json.dump(to_push, f, ensure_ascii=False, indent=2)

client = SearchClientSync(APPID, APPKey)
resp = client.replace_all_objects("mx-space", to_push)
print(resp)

如果你用的是其他博客框架,看到這裡就夠了,希望能給你提供點思路。

很好,用 Python 修改搜索索引後重新提交到 Algolia 並在 mx-space 後台啟用搜索功能,來試一試搜索超出限制的JPEG 編碼細節吧。
怎麼沒有結果?怎麼後台又報錯了?

17:03:46  ERROR   [Catch]  Cast to ObjectId failed for value "1234567_0" (type string) at path "_id" for model "posts"
  at SchemaObjectId.cast (entrypoints.js:1073:883)
  at SchemaType.applySetters (entrypoints.js:1187:226)
  at SchemaType.castForQuery (entrypoints.js:1199:338)
  at cast (entrypoints.js:159:5360)
  at Query.cast (entrypoints.js:799:583)
  at Query._castConditions (entrypoints.js:765:9879)
  at Hr.Query._findOne (entrypoints.js:768:430)
  at Hr.Query.exec (entrypoints.js:784:5145)
  at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
  at async Promise.all (index 0)

來編輯 mx-space 代碼吧#

從上述 log 很容易看出,mx-space 使用ObjectId作為了索引,而不是id,定位到代碼中的這裡:

https://github.com/mx-space/core/blob/20a1eef/apps/core/src/modules/search/search.service.ts#L164-L165

將其修改如下即可。

https://github.com/Do1e/mx-space-core/blob/1d50851/apps/core/src/modules/search/search.service.ts#L167-L168

更進一步地完善#

僅僅如此也還是不太優雅,需要定時運行 Python 腳本將數據推送到 Algolia。既然都已經開始修改 mx-space 代碼了,不如一步到位把分頁集成進去算了,好在現在的各種 AI 能幫我快速上手原本不太會的編程語言。

/apps/core/src/modules/search/search.service.tsbuildAlgoliaIndexData()後添加下述代碼,邏輯與上述 Python 相同:

https://github.com/Do1e/mx-space-core/blob/1d50851/apps/core/src/modules/search/search.service.ts#L297-L333

重新構建 docker 鏡像,然後從官方鏡像切換過來就 OK 了!

不過原始版本還定義了 3 種事件(增、刪、改)觸發單個元素的推送,這裡我就懶得改了,直接把裝飾器(ts 裡應該叫什麼?我只知道 Python 是這麼叫的)移到pushAllToAlgoliaSearch就好了。

小插曲#

在編輯代碼的時候,我發現原來代碼中已經定義了超出限制長度後截斷。不過定義的是 100KB,看来開發者是付費玩家。個人覺得把這個設置為環境變量會更好,而不是寫死在代碼裡。

https://github.com/mx-space/core/blob/20a1eef/apps/core/src/modules/search/search.service.ts#L370

2024/12/21: 作者更新了可配置的截斷,不過我還是更喜歡分頁提交,畢竟可以全文搜索嘛。

https://github.com/mx-space/core/commit/6da1c13799174e746708844d0b149b4607e8f276

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。