# 从 Algolia 迁移到 Typesense

如果您当前正在使用 Algolia 并计划迁移到 Typesense,本指南旨在为您提供一些有用的提示,帮助您顺利完成过渡。 这些内容是基于我们观察到的 Algolia 用户在切换到 Typesense 时常见的情况整理而成。

# 时间线

我们从考虑切换到 Typesense 的 Algolia 用户那里最常被问到的问题是:迁移通常需要多长时间。

中位迁移时间线约为 2-3 周。

目前最快的记录是 3 小时就完成了从 Algolia 到 Typesense 的生产环境切换(当然这是极少数情况)。 另一方面,也有少数用户花费了 1-1.5 个月的时间,因为他们希望先通过功能标志将 Typesense 部署到一小部分流量,然后在数周内逐步将流量转移到 Typesense,同时密切监控指标并进行微调。

如果您使用的是 Algolia 的 InstantSearch (opens new window) UI 组件,那么迁移时间往往处于较短的一端, 因为我们提供了一个适配器库,安装到前端应用后可以自动将查询转换为 Typesense 格式。这样您就可以保持现有的 UI 组件不变,最快在 30 分钟到 1 小时内完成迁移。

因此,主要的工作是将您的 JSON 文档推送到 Typesense 而不是 Algolia,并考虑下面提到的差异点。 您还需要考虑同义词和查询规则的迁移,Algolia 和 Typesense 都提供了导出和导入这些数据的 API。

# 架构

Algolia 和 Typesense 在架构上非常相似——两者都是内存搜索引擎,针对闪电般快速的搜索进行了优化。

# API 兼容性

虽然 Typesense 是 Algolia 的开源替代方案,能够提供同样即时的输入即搜索体验,但它在一些关键方面对 Algolia 进行了改进。因此,尽管您可能会发现 Typesense 和 Algolia 有许多相似的概念,但我们基于第一性原则设计了 Typesense 的功能集,所以这些 API 在设计中并不相互兼容。

# 类型检查

Typesense 鼓励您为文档定义模式(schema),然后对索引的文档进行类型检查,以确保索引的数据保持一致,避免意外情况或细微错误。这与 C++、Go、Rust、Java、Kotlin、Swift、Typescript 等强类型编程语言中的类型检查优势非常相似。

在 Algolia 中,您可以发送任何 JSON 数据进行索引,数据类型会按原样保留,即使文档之间存在不一致。因此,您可能会有一个文档的 "timestamp" 字段是字符串类型,而另一个文档的 "timestamp" 字段是整数类型。这在使用不同类型的过滤器时可能会导致一些问题。

虽然 Typesense 不允许同一字段在不同文档中存在不同的数据类型,但您可以配置 Typesense 根据索引的文档自动检测模式(参见 自动模式检测)。您还可以在导入文档时使用 coerce_or_reject 参数,配置 Typesense 根据检测到的模式自动尝试转换数据类型。

# 同步写入 API

在 Algolia 中,所有写入 API 调用都会在内部排队并异步应用到索引中。您需要通过轮询写入状态来了解每个写入操作的状态。根据数据集的大小,您可能会在发起写入 API 调用和搜索时显示在索引之间存在延迟。

在 Typesense 中,所有写入 API 调用都是同步的。无需轮询即可了解写入状态。如果 API 调用成功,意味着数据已写入集群中的大多数节点并可用于搜索。这也意味着包含大批量数据的这些同步写入 API 调用将需要更长时间来完成数据摄入。

根据并发索引的数据量,如果超过配置的阈值,Typesense 可能会返回 HTTP 503 Lagging or Not Ready 消息,以确保在高容量写入期间搜索操作不受影响。此时,您需要在稍后的时间点暂停后重试写入 API 调用。

# 功能对等性

Typesense 目前与 Algolia 的功能对等性约为 85%(参见功能对比矩阵 (opens new window))。我们计划根据从 Algolia 切换到 Typesense 的用户反馈来缩小这一差距。

# Algolia 有而 Typesense 没有的关键功能

  • 服务端 AB 测试(可以通过使用 AB 测试框架客户端实现,并根据用户的分桶标识使用不同的集合)
  • 开箱即用的 AI/ML 功能
    • 动态同义词建议
    • 开箱即用的事件追踪
    • 开箱即用的用户级个性化(可以通过将机器学习模型的输出导入 Typesense 来实现。详见此处
    • 开箱即用的推荐功能(这里提供了如何使用机器学习模型和向量搜索在 Typesense 中实现推荐的指南)。

# Typesense 相比 Algolia 的独特功能

  • 单一索引支持多重(严格)排序(在 Algolia 中,每个严格排序条件如价格升序、价格降序等都需要创建重复索引)
  • 字段数据类型验证(类似强类型语言),在文档索引时防止不一致数据进入索引(如需 Algolia 类似行为可关闭此功能)
  • 搜索时可指定字段数值权重,给予特定字段更高优先级
  • 支持存储和查询同一记录中的多个地理坐标字段(纬度/经度),并在单次查询过滤时使用逻辑运算符组合它们
  • 支持存储自有机器学习模型的向量,并进行最近邻搜索
  • 支持使用嵌入模型(如 OpenAI、PaLM API 或内置模型 S-BERT、E-5 等),实现混合搜索(语义+关键词)并与大语言模型(LLMs)集成
  • 支持以对话式响应返回结果(内置 RAG 功能),基于您的 JSON 数据生成自然语言回答
  • 支持为集合创建别名(类似符号链接)
  • 动态搜索参数配置:Algolia 中需要在索引级别配置的多数参数,在 Typesense 中可在搜索时动态配置,提供更高灵活性
  • 无限制设计:记录大小、最大索引尺寸、同义词数量、规则数量或索引数量均无硬性限制
  • 支持自托管部署
  • 可运行于持续集成环境(得益于自托管特性)
  • 完全开源

# 等效功能与概念对照

以下是 Algolia 和 Typesense 中常见功能及概念的术语对照表。

# 术语对照

Algolia Typesense
每个被索引的 JSON 对象称为记录(record) 每个被索引的 JSON 对象称为文档(Document)
记录的集合称为索引(Index) 记录/文档的集合称为集合(Collection)
分布式搜索网络(Distributed Search Network) 搜索交付网络(Search Delivery Network)(在 Typesense Cloud 中)
神经搜索(NeuralSearch) 混合搜索(Hybrid Search),本质上是语义搜索+带自动嵌入生成的关键词向量搜索

# 功能对比

Algolia Typesense
认证通过 Application IDAPI Key 实现 认证通过 x-typesense-api-key 实现
安全或虚拟 API 密钥 范围限定的 API 密钥
导入记录(无验证和模式) 创建带有 自动模式检测 的集合,并使用 coerce_or_reject 导入文档
查询规则 覆盖规则(也称为策展功能,Typesense Cloud 还提供了拖拽管理界面用于覆盖规则)
查询建议 (opens new window) 在 Typesense 中也称为 查询建议,可通过 分析规则 创建
商品推广 通过覆盖规则 提升或排除结果,或在搜索时使用 pinned_hitshidden_hits 搜索参数
动态过滤 通过覆盖规则实现动态过滤
用于排序的虚拟索引副本 在 Typesense 中,单个集合可以通过 sort_by 处理多种排序方式,因此不需要虚拟索引副本
多索引搜索(联合搜索或 multipleQueries multi_search
Algolia 中的排名和相关性 (opens new window) Typesense 中的排名和相关性

Typesense 的一个关键区别是我们简化了相关性调优体验,大多数用例开箱即用,并尽量减少了需要调整的参数数量。
记录过滤 (opens new window) [filter_by 搜索参数可过滤文档
记录分面 (opens new window) facet_by 搜索参数可对文档进行分面
记录分组 (opens new window) group_by 搜索参数可对文档进行分组
地理搜索(使用 aroundRadius, aroundLatLng Typesense 的地理搜索
地理搜索(使用 insidePolygon 多边形内的地理搜索
地理搜索(使用 insideBoundingBox 如果边界框的对角端点坐标为 [A,X][B,Y],您可以通过交换经纬度获取边界框的另外两个坐标:[A,Y][B,X]。有了这 4 个坐标,就可以使用 Typesense 的 多边形地理搜索功能 在边界框内进行搜索。
使用 aroundPrecision 控制地理搜索精度 geo_precisionexclude_radius

# 配置对比

Algolia Typesense
searchableAttributes 所有需要建立索引的字段/属性在创建集合时配置,然后可以在搜索时通过query_by参数动态选择使用字段子集。
attributesForFaceting 用于分面和过滤 通过在集合模式中为字段设置facet: true来启用分面功能,搜索时可通过facet_by调整
在Typesense中,过滤字段不需要设置为分面字段。
unretrievableAttributes 可通过创建范围API密钥并在其中嵌入exclude_fields搜索参数来配置
attributesToRetrieve 可通过创建范围API密钥并在其中嵌入include_fields搜索参数来配置
attributeForDistinctdistinct 可通过group_bygroup_limit搜索参数配置
separatorsToIndex 创建集合时的symbols_to_index设置
removeWordsIfNoResults drop_tokens_threshold搜索参数
disablePrefixOnAttributes prefix=false,false,true搜索参数,对应query_by中的字段
disableTypoToleranceOnAttributes num_typos=false,false,true搜索参数,对应query_by中的字段
customRanking (opens new window) 可在sort_by参数中指定最多3个排序字段。

例如:sort_by=_text_match(buckets: 10):desc,custom_field_1:desc,custom_field_2:desc

从v0.23.0开始,这会将结果集分为10个桶(从最相关到最不相关),强制每个桶内的结果并列,然后使用自定义排序字段在每个桶内进行排序。

# API 接口对比

Algolia Typesense
使用 saveObjects 导入/索引文档 使用 /collections/<collection_name>/documents/import 端点配合 action=upsert 参数导入文档
使用 partialUpdateObjects 并设置 createIfNotExists: true 使用 /collections/<collection_name>/documents/import 端点配合 action=emplace 参数导入文档 (从 v0.23.0 版本开始支持)
使用 browseObjects (opens new window) 导出记录 使用 /collections/collection_name/documents/export 端点导出文档
searchForFacetValues (opens new window) facet_query 搜索参数

# 迁移前端 UI 组件

Algolia 构建并开源了一套针对 Vanilla JS、React、Vue 和 Angular 的搜索 UI 库,名为 InstantSearch (opens new window)

Typesense 通过 typesense-instantsearch-adapter (opens new window) 支持相同的 InstantSearch 组件。您只需通过 npmyarn 将适配器安装到应用程序中并进行配置 (opens new window),现有的 UI 组件就能与您的 Typesense 集群配合工作,大多数情况下无需额外修改。

少数组件需要微小调整 (opens new window)才能与 Typesense 一起使用。

# 将数据从 Algolia 迁移至 Typesense

通常您会希望更新当前向 Algolia 发送 JSON 数据的应用后端,使其将相同数据发送至 Typesense。这样您就能直接从主数据存储向 Typesense 发送数据。

但如果您想快速一次性将 Algolia 中的数据导出至 Typesense 进行探索或数据回填,可按以下步骤操作:

# 步骤 1:从 Algolia 导出数据

安装 Algolia CLI (opens new window) 后运行:

algolia objects browse YOUR_INDEX_NAME > documents-raw.jsonl

此命令会将 Algolia 记录导出为 JSONL 文件。

# 步骤 2:转换数据

# ID 字段

Algolia 使用名为 objectId 的字段来唯一标识记录,而 Typesense 使用名为 id 的字段实现相同目的。

我们可以使用 jq (opens new window) 工具将下载的 JSONL 文件中 objectId 字段的值复制到新字段 id 中:

jq -c '(to_entries[] | select(.key | ascii_downcase == "objectid")).key as $key | .["id"] = .[$key]' documents-raw.jsonl > documents-with-ids.jsonl

# 时间戳(可选)

如需按日期/时间戳排序,需要将 ISO8601 格式的日期/时间戳转换为 Unix 时间戳(纪元时间)。

以下是转换命令:

jq -c 'if .your_iso_timestamp_field then .your_iso_timestamp_field |= (sub("\\.[0-9]+"; "") | strptime("%Y-%m-%dT%H:%M:%SZ") | mktime) else . end' documents-with-ids.jsonl > documents.jsonl

# 步骤 3:创建集合

按照此处的说明在 Typesense 中创建集合。

对于 Algolia 中配置为可分面(facetable)的字段,需要在 Typesense 中设置 facet: true

这里 (opens new window)有一个实用工具,可以帮助您从数据集的 JSON 示例对象生成 Typesense 集合模式的初稿:

npx typesense-collection-schema-generator@latest <path_to_input_json_document_file> <path_to_output_typesense_collection_schema_json_file>

# 步骤 4:导入文档

现在你可以使用以下代码片段,将转换好的 JSONL 文件导入到你的Typesense 集合中:

export TYPESENSE_API_KEY=xyz
export TYPESENSE_HOST=xxx.a1.typesense.net
export TYPESENSE_PROTOCOL=https
export TYPESENSE_COLLECTION_NAME=YOUR_INDEX_NAME


# 我们使用 `parallel` 命令并行化导入(请确保已安装该命令):

parallel --block -5 -a documents.jsonl --tmpdir /tmp --pipepart --cat 'curl -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" -X POST -T {} "${TYPESENSE_PROTOCOL}://${TYPESENSE_HOST}/collections/${TYPESENSE_COLLECTION_NAME}/documents/import?action=upsert"'

提示

  • 增大命令中的 -5 数值可以减小每批次导入 Typesense 的数据块大小
  • 如果遇到 "Bad Request" 或 "Connection Refused" 错误,可能需要根据你的 shell 环境调整命令中的转义符和引号
  • 如果出现 404 错误,请确保在运行导入命令前已创建 Typesense 集合

# 步骤5:导入查询规则

如果您使用 Algolia 的 Query Rules(查询规则)功能来根据条件定制搜索结果,可以使用我们提供的这个工具来导入这些规则:

npx algolia-query-rules-to-typesense@latest <path/to/algolia_rules_export.json> <path/to/typesense_overrides_output.json>

要获取 Algolia 规则导出文件,请转到 Algolia 索引的 "Rules" 部分,您会找到一个下载图标将规则导出为 JSON 文件。

然后您可以使用 Typesense Overrides API 将这些转换后的 JSON 规则(typesense_overrides_output.json)导入到 Typesense 中。

# 地理分布式集群

Algolia 将其类似 CDN 的地理分布式搜索服务称为 Distributed Search Network (opens new window),并且仅作为付费附加功能提供给按年付费的客户。

在 Typesense Cloud 中,类似 CDN 的地理分布式搜索服务称为 Search Delivery Network(搜索分发网络),所有用户在创建新集群时都可以选择此配置。

# 定价模型

Algolia 按照记录数量和搜索次数(如果实现了即时搜索则为按键次数)收费,您需要为这两个维度中较大的数值付费,如果超出计划限制还需支付超额费用。 因此,如果您有高流量但记录数量少,或者低流量但记录数量多,您都需要为两者中较大的数值付费。

Typesense 是免费开源的,可以免费自托管。

Typesense 还提供名为 Typesense Cloud (opens new window) 的托管搜索服务。 Typesense Cloud 的定价基于您索引数据所需的 RAM 和 CPU 资源量,以及支撑预期并发流量所需的计算资源。 根据您选择的配置,采用固定的小时费率计费,外加标准带宽费用,类似于 AWS、GCP 等云服务商。 与 Algolia 不同,我们不收取每条记录或每次搜索的费用。只要您的集群能够承载,您可以随意增加流量或数据量。 根据我们的观察,从 Algolia 迁移到 Typesense Cloud 的用户可以节省 50% 到 95% 的搜索成本。

# Algolia 迁移支持

如果您计划从 Algolia 迁移到 Typesense Cloud,我们提供免费的 迁移咨询支持 (opens new window),根据您在 Algolia 的使用情况提供不同级别的服务。