Versun

对待生命,不妨大胆一点,因为我们终将失去它


created: 2025-03-17, updated: 2025-03-17

为博客添加 Algolia 搜索

原先我是使用 pg_search 包做搜索功能,但搜索效果很差,由于没有安装中文分词库,所以模糊搜索几乎不可用。
所以我想优化搜索功能,刚开始是准备自部署 meilisearch 服务端来完成,初始代码写完后,却一直无法完成 ActionText 的索引,遂放弃。
随后简单对比了几家提供搜索功能的服务商,只有 Algolia 提供了免费层,1万次/月的搜索次数,1百万条索引记录,非常慷慨,而且它也是 Hacker News全文搜索后端,很好用。
Algolia 支持的数据导入方式非常丰富,除了 api 外,还提供了主流 CMS 的插件、爬虫、文件的导入方法:
algolia import data methods

这里我使用了 Rails 的SDK进行导入,步骤如下:
1. 添加包到 Gemfile 并安装
gem "algoliasearch-rails"
2. 添加初始化文件:config/initializers/algoliasearch.rb
AlgoliaSearch.configuration = {
    application_id: ENV["ALGOLIASEARCH_APP_ID"],
    api_key: ENV["ALGOLIASEARCH_API_KEY"],
  # pagination_backend: :will_paginate # 如果使用了 will_paginate 分页,可添加该行
}
3. 在需要索引的模型中,添加搜索功能,比如我在 Article 模型中添加的代码:
class Article < ApplicationRecord
  has_rich_text :content
  # ...
  include AlgoliaSearch
  algoliasearch if: :should_index? do
      attribute :title, :slug, :description, :plain_content
      attribute :plain_content do
        text = content.to_plain_text
        algolia_max_characters = ENV.fetch("ALGOLIA_MAX_CHARACTERS", "3500").to_i
        if text.size > algolia_max_characters
          text = text.truncate(algolia_max_characters)
        end
        text
      end
      searchableAttributes [ "title", "slug", "description", "plain_content" ]
    end

  def should_index?
      status == "publish" || status == "shared"
  end   
  #...
end
需要注意的是,免费层的每个索引记录大小需要限制在 10kb 以内,但我使用 bytesize 方法时无法成功添加,提示无效的 object,所以这里我直接使用字符数。
然后在 articles_controller 中添加相关处理代码
 def index
    respond_to do |format|
      format.html {
        @page = params[:page].present? ? params[:page].to_i : 1
        @per_page = 10

        if params[:q].present?
            # 使用Algolia搜索
            algolia_results = Article.algolia_search(params[:q], { hitsPerPage: @per_page, page: @page - 1 }) # Algolia页码从0开始

            # 获取Algolia的结果总数
            @total_count = algolia_results.size
            @articles = algolia_results
        else
          # 不搜索,只分页
          @articles = Article.published
                             .includes(:rich_text_content)
                             .order(created_at: :desc)
                             .paginate(page: @page, per_page: @per_page)
          @total_count = @articles.total_entries
        end
      }
# ...
    end
  end
在 view 中添加搜索代码
<%= form_tag root_path, method: :get, class: 'search-form' do %>
    <%= search_field_tag :q, params[:q], placeholder: 'Search...' %>
    <%= submit_tag 'Search' %>
<% end %>
最后,设置好认证信息和环境变量后,在 rails console 中进行首次索引:Article.reindex!
如果想清除所有索引,则执行:Article.clear_index!
完成

Discussion on Mastodon, X, Bluesky.