介绍 Typesense
网上的资料可能比较少,最近花时间整理了一下Typesense的相关介绍,和基础使用,感兴趣可以点点关注。
在现代的搜索引擎技术中,Typesense 是一个崭露头角的开源搜索引擎,它专为开发者设计,致力于提供快速、精准和容易部署的全文搜索体验。Typesense 特别适用于需要高效搜索功能的应用程序,比如电商平台、文档管理系统以及内容管理系统。它以其简洁的API、强大的功能以及易用性,逐渐成为众多开发者的首选。
Typesense 具备如下几个显著特点:
1. 即时搜索:当用户输入时,Typesense 能够实时提供搜索建议和结果。
2. 自动纠错:它能够识别拼写错误并返回最相关的结果。
3. 易于部署:通过 Typesense 的配置文件和Docker支持,开发者能够迅速在本地或云端搭建搜索服务。
4. 全文检索:支持多字段的全文搜索,并提供精准的排序和过滤选项。
接下来,我们将详细介绍如何在您的系统中安装和配置 Typesense。
与 ELasticSearch 对比
| Typesense | Algolia | ElasticSearch | Meilisearch | |
| 源代码 | 完全开源 | 专有闭源 | 源可用,采用 SSPL 许可 | 完全开源 |
| 首次提交 | 2015 | 2012 | 2010 | 2018 |
| 构建使用 | C++ | C++ | Java | Rust |
| 核心搜索算法 | Built from the ground-up | Built from the ground-up | 基于 Lucene 构建 | Built from the ground-up |
| 最适合的用途 | 实时输入即时搜索体验,适用于可放入 RAM 的数据集,最大可达 24 TB(或当前商业可用的 RAM 大小)。 | 实时输入即时搜索体验,适用于最大 128 GB 的数据集。 | 通用搜索和聚合,适用于 PB 级数据集(如日志数据) | 实时输入即时搜索体验,适用于不需要高可用容错设置的用例。建议为理想性能提供足够的 RAM 以将整个数据集存放在 RAM 中。 |
| 主索引位置 | RAM | RAM | 磁盘,带 RAM 缓存 | 磁盘带内存映射文件 |
更多对比可参考:点击跳转[1]
tips: 很遗憾的一点是,截止到发文,Typesense开源版目前没有像Kibana这种类似的工具。

系统配置
Typesense 是一种内存数据存储,针对快速、低延迟检索进行了优化。为了做到这一点,它将整个搜索索引存储在内存中,并将原始数据的副本存储在磁盘上。
所需内存计算
Typesense 进程本身非常轻量级,在没有数据索引的情况下仅占用大约 20MB
的 RAM。所需的 RAM 量完全取决于您索引的数据的大小。
关键字(keyword)搜索
如果数据集的大小(仅包括要搜索的字段)为 X MB
,则通常需要 2X-3X MB
RAM 来在 Typesense 中索引数据。
例如:如果数据集大小为 5GB
,并且您想要搜索字段的子集,而该字段子集的大小为 1GB
,则您需要 2GB 到 3GB
的 RAM 来在内存中保存搜索索引。
可以在 Typesense 中存储未编制索引的字段(例如:您只想用于显示目的的字段) - 这些未索引的字段将存储在磁盘上,不计入 RAM 使用量。
举一个实际计算需要RAM的方式
假设你有一个像这样的 JSON 文档:
{
"album_name": "John Denver Rare and Unreleased",
"country": "US",
"genres": ["country"],
"id": "31401733",
"primary_artist_name": "John Denver",
"release_date": 1104537600,
"release_decade": "2010s",
"release_group_types": [
"Album"
],
"title": "Annie's Song",
"track_id": "58ac90d0-d6fe-4395-9e65-f714ae4c23c0",
"urls": []
}
如果你只想在 album_name
和 primary_artist_name
这两个字段上进行搜索,并且你还想在 genres
和 release_date
这两个字段上进行过滤,那么在计算 RAM 使用量时,你只需要考虑这些字段的值的大小作为一条记录的大小。
也就是说,虽然你的文档中有像 track_id
、urls
、release_decade
等其他字段,但因为你不在这些字段上进行搜索或过滤,它们不会计入 RAM 的使用量。
我们假设平均情况下,一个专辑名称(album_name
)可以有 100 个字符(即 100 字节),主要艺术家名称(primary_artist_name
)可以有 50 个字符(即 50 字节),genres
字段的值可能有 50 个字符(即 50 字节),release_date
是一个整数,占用 4 字节。
因此,我们平均每条记录的大小为:100 + 50 + 50 + 4 = 204 字节,约等于 0.204 KB。
字段名称的长度不会影响 RAM 使用,因为字段名称在索引中并不会重复存储。
如果我们有 100 万条记录,那么我们总的数据集大小为:0.204 KB * 100 万条记录 = 204 MB。因此,我们的 RAM 消耗大约在 204MB * 2 = 408MB(低估)和 204MB * 3 = 612MB(高估)之间。
向量搜索
为向量搜索索引文档时,每个向量都需要 7 个字节的内存。
因此,如果嵌入模型返回 N 维向量,并且有 X 条记录,则内存消耗将为 7 个字节 * N 个维度 * X 个记录
字节。
例如:如果您使用的是具有 384 个维度且有 100K 条记录的 S-BERT 模型,则内存消耗将为 7 个字节 * 384 个维度 * 100,000 条记录 = 268.8 MB
。
所需CPU容量计算
CPU 容量对于处理并发搜索流量和索引操作非常重要。
tips:Typesense 至少需要
2 个 vCPU
的计算能力才能运行。
Typesense 被设计为高度可扩展的开箱即用,无需额外配置。它会自动利用所有可用的计算容量。因此,特定 CPU 配置每秒可以处理的最大请求数完全取决于您的搜索查询模式以及索引数据的大小。
虽然很难对理想的 CPU 容量做出准确的建议,但由于这取决于您的数据,但这里有一些参考数据点可以让您更好地了解 CPU 需求:
• 一个 4vCPU Typesense 节点每秒能够处理 104 个并发搜索查询,处理 220 万条记录。
• 一个 4vCPU Typesense 节点每秒能够处理 46 个并发搜索查询,处理 2800 万条记录。
• 一个 8vCPU 3 节点 Typesense 集群每秒能够处理 250 个并发搜索查询,处理 300 万条记录。
所需磁盘
Typesense 将原始数据的副本存储在磁盘上,然后使用此数据构建内存索引。然后,在搜索时,在确定要在 API 响应中返回的最后一组文档后,从磁盘中获取这些文档并将它们放入 API 响应中。
您需要足够的磁盘空间,至少是原始数据集的大小,才能运行 Typesense。建议使用 SSD。
选择节点数
Typesense 可以在单节点配置中运行,也可以在高可用性多节点集群配置中运行。
建议在生产环境中运行高可用性 3 节点或 5 节点配置,以确保搜索服务能够灵活应对不可避免的基础结构问题。
Typesense 使用 Raft 作为其共识算法,由于 Raft 需要达到节点的法定人数才能达成共识,因此您至少需要运行 3 个节点才能容忍 1 个节点的故障。运行 5 节点集群可以容忍最多 2 个节点的故障,但代价是写入延迟更高。
关于高可用可以参考官方介绍:https://typesense.org/docs/guide/high-availability.html
集合概念
typesense的存储结构层次如下:
[Typesense Cluster] ---has-many---> [Collections] ---has-many---> [Documents] ---has-many---> [Attributes/Fields]
• 将Collections直接理解为关系型数据库中的**表(Table)**即可。
•
Documents
理解为每一条(行)数据 。•
Attributes/Fields
每一个具体字段。
多租户应用
假设您有一个社交媒体应用程序,并且您希望限制用户仅通过自己朋友的名字进行搜索。
您可以将所有用户存储在一个名为 Typesense 中的 say users
的集合中,并在每个用户文档中存储一个名为 say friends_with_user_ids
的数组属性。然后,您可以为每个用户生成单独的作用域搜索 API 密钥[2],并将该密钥限制为只能访问在 friends_with_user_ids
属性中包含此用户 ID 的记录。
分片集合
他与ElasticSearch分片不同,在Typesense中实现分片功能,需要我们主动创建分片集合。
Typesense 已经过测试,并已用于每个集合的 100 万个文档。但是,随着集合中文档数量的增加和/或筛选复杂性的增加,搜索响应时间往往与集合的大小相关。这与关系数据库中表的大小如何影响大规模的查询处理时间非常相似。
如果您碰巧注意到 Typesense 中的任何大规模写入/读取性能问题,提高性能的一个好方法是使用 user_id
、created_at``、Country
等属性将单个集合分片为多个集合。因此,例如,您可以创建 users_usa
、users_canada
users_uk
等集合,并将每个国家/地区的用户放置在单独的集合中。或者,您可以创建诸如 users_1
、users_2
等集合......users_n
所有具有 user_id mod n
user_id的用户都放在相应的集合中。
另一个方法是在多租户应用程序中 ,如果有一个特定用户的数据足够大,则可以将该用户的数据移动到新集合中,同时将所有其他用户的数据保留在主集合中。
安装 Typesense 的详细步骤
环境准备
在开始安装之前,确保你的系统满足以下条件:
• 一个现代的操作系统 (Linux, macOS, Windows)。
• 安装了 Docker 和 Docker Compose(如果你打算通过 Docker 安装)。
• 拥有管理员权限来安装和配置软件。
安装 Typesense
1. 通过 Docker 安装 Typesense
Docker 是安装和管理 Typesense 最简单的方法。以下是通过 Docker 安装 Typesense 的步骤:
步骤1:运行 Typesense 容器
使用以下命令来拉取并运行 Typesense 的 Docker 容器:
docker run -d --name typesense-server \
-p 8108:8108 \
-v /tmp/typesense-data:/data \
typesense/typesense:26.0 \
--data-dir /data \
--api-key=xyz
这里的 -p 8108:8108
表示将容器的 8108 端口映射到主机的 8108 端口。--api-key=xyz
是用于访问 Typesense 的 API 密钥,你可以将 xyz
替换为你的自定义密钥。
这里可以查看到docker镜像的版本列表:https://hub.docker.com/r/typesense/typesense/tags
步骤3:验证 Typesense 是否运行
打开浏览器,访问 http://localhost:8108/health
。如果显示 {"ok":true}
,说明 Typesense 已经成功运行。
2. 通过二进制文件安装 Typesense
如果你不希望使用 Docker,你也可以通过下载 Typesense 的二进制文件来安装它。
步骤1:下载 Typesense 二进制文件
访问 Typesense 官方发布页面[3],下载适合你系统的版本(例如 Linux 或 macOS)。
步骤2:解压并运行 Typesense
下载完成后,解压文件:
tar -xzf typesense-server-VERSION.tar.gz
cd typesense-server-VERSION
运行 Typesense:
./typesense-server --data-dir /path/to/data --api-key=xyz
步骤3:验证安装
同样地,访问 http://localhost:8108/health
,查看是否显示 {"ok":true}
。
配置 Typesense
Typesense 提供了许多配置选项来优化搜索服务。你可以通过编辑 Typesense 的配置文件或传递命令行参数来配置这些选项。
• data-dir: 设置存储数据的目录。
• api-key: 设置访问 Typesense API 的密钥。
• port: 设置服务器监听的端口(默认是 8108)。
• log-dir: 指定日志文件的存储目录。
到此服务安装完成,下面就是具体的使用方法。
Typesense 的基本使用
集合
创建集合
有两种方式:
• 预定义字段
• 自动检测数据中包含字段
在正式环境中通常是使用预定义设置的方式来定义好我们需要的字段。
预定义字段
创建方式
curl "http://localhost:8108/collections" \
-X POST \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"name": "companies",
"fields": [
{"name": "company_name", "type": "string" },
{"name": "num_employees", "type": "int32" },
{"name": "country", "type": "string", "facet": true }
],
"default_sorting_field": "num_employees"
}'
创建集合的可选参数
| 参数 | 必填 | 描述 |
| name | 是 | 要创建的集合的名称。 |
| fields | 是 | 您希望为查询[4]、过滤[5]、分面[6]、分组[7]和排序[8]索引的字段列表。对于每个字段,您至少需要指定它的name和 type[9]。 示例: {"name": "title", "type": "string", "facet": false, "index": true}name可以是一个简单的字符串,如 "name": "score"。您也可以使用正则表达式来指定匹配某个模式的字段名。例如,如果您想指定所有以 score_开头的字段应为整数,可以将名称设置为 "name": "score_.*"。 声明字段为可选 通过设置 "optional": true,可以将字段声明为可选字段。 声明字段为分面字段 通过设置 "facet": true,可以将字段声明为分面字段。分面字段会被逐字索引,无需任何分词或预处理。例如,如果您在构建一个产品搜索, color和 brand可以定义为分面字段。一旦在架构中启用了字段的分面功能,就可以在 facet_by搜索参数[10]中使用它。 启用词干提取 词干提取允许您处理同一词根的常见词语变体(单数/复数、时态变化)。例如:当启用词干提取时,搜索 walking时,也会返回包含 walk、 walked、 walks等变体的结果。 在索引和查询过程中,通过设置 "stem": true启用字段内容的词干提取。存储在磁盘上的实际值不受影响。 我们使用Snowball词干提取器[11]。词干提取器的语言选择是根据与字段关联的 locale属性的值自动完成的。 声明字段为非索引字段 通过设置 "index": false,可以将字段设置为非索引字段(不能搜索/排序/过滤/分面)。这在与自动架构检测[12]一起使用时很有用,如果您需要排除某些字段的索引[13]。 防止字段存储在磁盘上: 设置 "store": false可确保在文档保存到磁盘之前从文档中移除字段值。 配置特定语言的分词: Typesense使用的默认分词器适用于大多数语言,尤其是那些用空格分隔单词的语言。然而,根据用户反馈,我们为以下语言添加了特定语言的自定义功能。您可以通过在字段定义中设置一个名为 locale的字段来启用这些自定义功能。例如: {name: 'title', type: 'string', locale: 'ja'}将为名为 title的字段启用日语的本地化自定义功能。 以下是所有特定语言的自定义列表:
|
| token_separators | 否 | 用于将文本分割成单个单词的符号或特殊字符的列表,_除此之外_,还包括空格和换行符。 例如:您可以将 -(连字符)添加到此列表中,以便将 non-stick这样的单词在连字符处拆分并作为两个独立的单词进行索引。 阅读本指南文章[14],了解如何使用此设置的更多示例。 |
| symbols_to_index | 否 | 要索引的符号或特殊字符的列表。 例如:您可以将 +添加到此列表中,以便将单词 c++逐字索引。 阅读本指南文章[15],了解如何使用此设置的更多示例。 |
| default_sorting_field | 否 | 一个int32/float字段的名称,该字段决定在搜索期间未提供 sort_by子句时,搜索结果的排序顺序。 此字段必须表示某种类型的受欢迎程度。例如,在产品搜索应用程序中,您可以定义 num_reviews字段作为 default_sorting_field,以默认情况下将评论最多的产品排在前面。 此外,当搜索查询中的一个词与多个可能的词匹配时(无论是在前缀(部分词)搜索期间还是由于拼写错误),此参数用于对这些同样匹配的记录进行排序。 例如,搜索“ap”时,将匹配“apple”、“apply”、“apart”、“apron”或数据集中所有以“ap”开头的数百个类似单词。此外,搜索“jofn”时,将匹配“john”、“joan”和数据集中所有类似的1个拼写错误的变体。 出于性能原因,Typesense默认只考虑前 4个前缀或拼写变体(可以通过 max_candidates[16]搜索参数配置默认值为 4)。 如果在集合架构中未指定 default_sorting_field,则“top”被定义为具有最多匹配记录的前缀或拼写变体。 但假设您的每条记录中都有一个名为 popularity的字段,并且您希望Typesense使用该字段的值来定义“top”记录,您可以将该字段设置为 default_sorting_field: popularity。然后,Typesense将使用该字段的值来获取最受欢迎的前 max_candidates数量的术语,并随着用户输入更多字符,它将进一步优化搜索,始终将最受欢迎的前缀排在最前面。 |
字段参数配置
| 参数 | 必填 | 描述 |
| name | 是 | 字段的名称。 |
| type | 是 | 字段的数据类型(请参阅下方的类型列表部分)。 |
| facet | 否 | 启用字段的分面功能。默认值:false。 |
| optional | 否 | 当设置为true时,字段可以有空值、null值或缺失值。默认值: false。 |
| index | 否 | 当设置为false时,该字段不会在任何内存索引(例如搜索/排序/过滤/分面)中进行索引。默认值: true。 |
| store | 否 | 当设置为false时,字段值将不会存储在磁盘上。默认值: true。 |
| sort | 否 | 当设置为true时,该字段将可排序。数字字段默认值为 true,其他情况默认值为 false。 |
| infix | 否 | 当设置为true时,字段值可以进行中缀搜索。这会消耗大量内存。默认值: false。 |
| locale | 否 | 用于配置特定语言的分词,例如日语使用jp。默认值: en,该设置也广泛支持大多数欧洲语言。 |
| num_dim | 否 | 将此值设置为非零值以将float[]类型的字段视为向量字段。 |
| vec_dist | 否 | 用于向量搜索的距离度量。默认值:cosine。您也可以使用 ip表示内积。 |
| reference | 否 | 在另一个集合中应该链接到此集合的字段名称,以便在查询期间可以进行联接。 |
| range_index | 否 | 启用优化数值字段范围过滤的索引(例如:rating:>3.5)。默认值: false。 |
| stem | 否 | 在内存中索引之前进行词干提取。默认值:false。 |
字段可选类型
Typesense 允许您索引以下类型的字段:
type | 描述 |
string | 字符串值 |
string[] | 字符串数组 |
int32 | 最大值为 2,147,483,647 的整数 |
int32[] | int32数组 |
int64 | 大于 2,147,483,647 的整数 |
int64[] | int64数组 |
float | 浮点数/小数 |
float[] | 浮点数/小数数组 |
bool | true或 false |
bool[] | 布尔值数组 |
geopoint | 以 [lat, lng]格式表示的经纬度。更多信息请阅读此处[17]。 |
geopoint[] | 以 [[lat1, lng1], [lat2, lng2]]格式表示的经纬度数组。更多信息请阅读此处[18]。 |
object | 嵌套对象。更多信息请阅读此处[19]。 |
object[] | 嵌套对象数组。更多信息请阅读此处[20]。 |
string* | 特殊类型,自动将值转换为 string或 string[]。 |
image | 特殊类型,用于指示用于图像搜索[21]的 base64 编码图像字符串。 |
auto | 特殊类型,基于添加到集合的文档自动推断数据类型。参见自动模式检测[22]。 |
自动检测识别字段与类型
如果您的字段名称是动态的并且预先不知道,或者您只是想保持简单并索引默认情况下在文档中发送的所有字段,那么自动架构检测应该对您有所帮助。
您可以定义一个名为 .*
的通配符字段,并键入 auto
,以便在您将文档添加到[23]集合时让 Typesense 自动检测字段的类型。事实上,您可以使用任何 RegEx 表达式来定义字段名称。
{
"name": "companies",
"fields": [
{"name": ".*", "type": "auto" }
]
}
当以这种方式定义 .*
字段时,文档中的所有字段都将自动建立索引以进行搜索和过滤。
举一个具体案例
假设您为集合中的特定字段(或字段)(例如:popularity_score
)设置了 type:auto
,并将第一个文档发送为:
{
"title": "A Brief History of Time",
"author": "Stephen Hawking",
"popularity_score": 4200
}
由于 popularity_score
具有 type:auto,因此数据类型将在内部自动设置为 int64
。
当下一个文档的popularity_score
字段不是整数字段,而是字符串时会发生什么?例如:
{
"title": "The Hunger Games",
"author": "Suzanne Collins",
"popularity_score": "4230"
}
默认情况下,Typesense 将尝试将值强制(转换)为先前推断的类型。因此,在此示例中,由于第一个文档具有 popularity_score
的数值数据类型,因此第二个文档的popularity_score
字段将被强制转换为字符串中的整数。
但是,这可能并不总是有效 - (例如:假设值有字母,它不能被强制为整数)。在这种情况下,当 Typesense 无法将字段值强制转换为先前推断的类型时,索引将失败并出现相应的错误。
获取集合信息
curl -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-X GET \
"http://localhost:8108/collections/companies"
返回结果
{
"created_at": 1723335077,
"default_sorting_field": "num_employees",
"enable_nested_fields": false,
"fields": [
{
"facet": false,
"index": true,
"infix": false,
"locale": "",
"name": "company_name",
"optional": false,
"sort": false,
"stem": false,
"type": "string"
},
{
"facet": false,
"index": true,
"infix": false,
"locale": "",
"name": "num_employees",
"optional": false,
"sort": true,
"stem": false,
"type": "int32"
},
{
"facet": true,
"index": true,
"infix": false,
"locale": "",
"name": "country",
"optional": false,
"sort": false,
"stem": false,
"type": "string"
}
],
"name": "companies",
"num_documents": 0,
"symbols_to_index": [
],
"token_separators": [
]
}
列出所有集合
curl -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
"http://localhost:8108/collections"
结果
[
{
"created_at": 1723335077,
"default_sorting_field": "num_employees",
"enable_nested_fields": false,
"fields": [
{
"facet": false,
"index": true,
"infix": false,
"locale": "",
"name": "company_name",
"optional": false,
"sort": false,
"stem": false,
"type": "string"
},
{
"facet": false,
"index": true,
"infix": false,
"locale": "",
"name": "num_employees",
"optional": false,
"sort": true,
"stem": false,
"type": "int32"
},
{
"facet": true,
"index": true,
"infix": false,
"locale": "",
"name": "country",
"optional": false,
"sort": false,
"stem": false,
"type": "string"
}
],
"name": "companies",
"num_documents": 0,
"symbols_to_index": [
],
"token_separators": [
]
}
]
删除集合
curl -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-X DELETE \
"http://localhost:8108/collections/companies"
当删除集合时,我们会执行磁盘压缩操作,该操作会回收已删除集合使用的磁盘空间。
修改集合
Typesense 支持更新除 id
字段之外的所有字段(因为它是 Typesense 中的一个特殊字段)。
添加删除字段
向 companies
集合添加新的 company_category
字段,并删除现有的 num_employees
字段。
或者,您也可以使用别名功能来执行零停机时间架构更改。curl "http://localhost:8108/collections/companies" \
-X PATCH \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"fields": [
{"name": "num_employees", "drop": true },
{"name": "company_category", "type": "string" }
]
}'
提示:
schema更新操作是同步阻塞操作。
当更新正在进行时,对该特定集合的所有写入都将等待schema更新完成。因此,我们建议一次更新一个字段。读取将照常提供服务,不会阻塞。或者,您也可以使用别名功能来执行零停机时间架构更改。
修改现有字段
由于 Typesense 目前只支持添加/删除字段,因此对现有字段的任何修改都应表示为删除 + 添加操作。除 id
字段外的所有字段都可以修改。
例如,要向 company_category
字段添加 facet
属性,我们将在同一个更改集中放置 + 添加它:
curl "http://localhost:8108/collections/companies" \
-X PATCH \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"fields": [
{"name": "company_category", "drop": true },
{"name": "company_category", "type": "string", "facet": true }
]
}'
TODO: 测试这么改字段,具体数据是否会丢失
使用别名
如果需要执行零停机时间schema更改,可以创建新集合,并使用“集合别名[24]”功能执行零停机时间切换到新集合:
假设您有一个名为 movies_jan_1
的集合,您想要更改其架构。
1. 首先,创建一个指向您的集合的别名。例如:创建一个名为
movies
的别名,指向movies_jan_1
。在应用程序中使用此集合别名来搜索/索引集合中的文档。2. 使用更新的集合架构创建新的带时间戳的集合。例如:
movies_feb_1
。3. 在您的应用程序中,暂时同时写入旧集合和并行的新集合 - 本质上是双重写入。例如:Send 既向
movies_jan_1
集合写入,也向新的movies_feb_1
集合发送写入。4. 现在,将主数据库中的数据更新插入到新集合中。例如:
movies_feb_1
。5. 将集合别名更新为现在指向新集合。例如:将
movies
更新为现在指向movies_feb_1
。6. 停止应用程序向旧集合发送写入操作,并删除旧集合,如我们的示例
movies_jan_1
。
文档
插入单条数据
curl "http://localhost:8108/collections/companies/documents" -X POST \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"id": "124",
"company_name": "Stark Industries",
"num_employees": 5215,
"country": "USA"
}'
更新插入(upsert)单条数据
如果文档已经存在,我们也可以替换具有相同 ID
的文档,或者如果不存在具有相同 ID
的文档,则创建一个新文档。
curl "http://localhost:8108/collections/companies/documents?action=upsert" -X POST \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"id": "124",
"company_name": "Stark Industries",
"num_employees": 5215,
"country": "USA"
}'
批量插入
curl "http://localhost:8108/collections/companies/documents/import?action=create" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-H "Content-Type: text/plain" \
-X POST \
-d '{"id": "124","company_name": "Stark Industries","num_employees": 5215,"country": "USA"}
{"id": "125","company_name": "Acme Corp","num_employees": 2133,"country": "CA"}'
action选项
| 取值 | 说明 |
| create (default) | 创建一个新文档。如果已存在具有相同 ID 的文档,则失败 |
| upsert | 如果已存在具有相同 ID的文档,则创建新文档或更新现有文档。需要发送整个文档。对于部分更新,请使用下面的 更新操作。 |
| update | 更新现有文档。如果具有给定 ID的文档不存在,则失败。您可以发送仅包含要更新的字段的部分文档。 |
| emplace | 如果已存在具有相同 ID的文档,则创建新文档或更新现有文档。您可以发送整个文档或部分文档进行更新。 |
返回结果
{"success":true}
{"success":true}
如果失败,响应行将包含相应的错误消息以及实际的文档内容。例如,第二个文档在以下响应中导入失败:
{"success": true}
{"success": false, "error": "Bad JSON.", "document": "[bad doc]"}
tips:
无论各个文档的导入结果如何,导入端点都将始终返回
HTTP 200 OK
代码。我们之所以这样做,是因为可能有一些文档在导入时成功,而另一些文档则失败了,我们不希望在这些部分场景中返回 HTTP 错误代码。为了保持一致性,我们在所有情况下都只返回 HTTP 200。
因此,请务必检查 API 响应中是否有任何
{success:false, ...}
记录,以查看是否有任何文档导入失败。
根据ID更新文档
curl "http://localhost:8108/collections/companies/documents/124" -X PATCH \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{
"company_name": "Stark Industries",
"num_employees": 5500
}'
通过查询更新
curl "http://localhost:8108/collections/companies/documents?filter_by=num_employees:>1000" -X PATCH \
-H "Content-Type: application/json" \
-H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
-d '{ "tag": "large" }'
删除文档
使用其 ID
从集合中删除单个文档。
curl -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" -X DELETE \
"http://localhost:8108/collections/companies/documents/124"
通过查询删除
curl -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" -X DELETE \
"http://localhost:8108/collections/companies/documents?filter_by=num_employees:>=100&batch_size=100"
以 JSONL 格式导出集合中的文档
curl -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" -X GET \
"http://localhost:8108/collections/companies/documents/export"
查询
curl -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
"http://localhost:8108/collections/companies/documents/search\
?q=stark&query_by=company_name&filter_by=num_employees:>100\
&sort_by=num_employees:desc"
查询参数
| 参数 | 必填 | 描述 |
| q | 是 | 要在集合中搜索的查询文本。 使用 *作为搜索字符串以返回所有文档。这在与 filter_by结合使用时通常很有用。 例如,要返回符合筛选条件的所有文档,请使用: q=*&filter_by=num_employees:10。 要在查询中明确排除某些词语,请在词语前加上 -运算符,例如 q: 'electric car -tesla'。 |
| query_by | 是 | 要查询的一个或多个字段名称。用逗号分隔多个字段:company_name, country字段的顺序很重要:在列表中较早字段上匹配的记录被认为比在列表中较晚字段上匹配的记录更相关。因此,在上面的示例中,匹配 company_name字段的文档会比匹配 country字段的文档排名更高。 只有集合模式中数据类型为 string或 string[]的字段可以在 query_by中指定。此外,通过在子字段的 string和 string[]字段上搜索,还支持 object和 object[]字段。关于嵌套对象字段的详细信息,您可以阅读 此处[25]。 |
| prefix | 否 | 指示查询中的最后一个单词应被视为前缀,而不是整个单词。这对于构建自动补全和即时搜索界面是必要的。将此设置为 false以禁用所有查询字段的前缀搜索。 您还可以控制每个字段的前缀搜索行为。例如,如果您正在查询3个字段,并且只想在第一个字段上启用前缀搜索,请使用 ?prefix=true,false,false。顺序应与 query_by中字段的顺序匹配。如果为 prefix指定了单个值,则该值将用于 query_by中指定的所有字段。 默认值: true(所有字段均启用前缀搜索)。 |
| infix | 否 | 中缀搜索可用于查找包含单词中间部分文本的文档。例如,我们可以使用中缀搜索来定位单词 AK1243XYZ6789中的字符串 XYZ。 注意: 中缀搜索适用于搜索电子邮件地址、电话号码、标识符等较小的字段,在这些字段中,中缀搜索特别有用。因此,中缀搜索仅使用查询中的第一个单词进行搜索。 由于中缀搜索需要额外的数据结构,您必须在每个字段上启用它,如下所示: {"name": "part_number", "type": "string", "infix": true }如果为该字段启用了中缀索引,则可以通过发送一个逗号分隔的字符串参数来在每个字段上进行中缀搜索。该参数可以有3个值: - off:禁用中缀搜索,这是默认值 - always:与常规搜索一起执行中缀搜索 - fallback:如果常规搜索没有产生结果,则执行中缀搜索 例如,如果您通过 ?query_by=title,part_number查询两个字段,您可以仅为 part_number字段启用中缀搜索,通过发送 ?infix=off,always(顺序应与 query_by中字段的顺序一致)。 还有两个参数允许您控制中缀搜索的范围: max_extra_prefix和 max_extra_suffix分别指定查询之前或之后可以存在的最大符号数量。例如:查询 "K2100" 在 "6PK2100" 中有 2 个额外符号。默认情况下,可以存在匹配的任何数量的前缀/后缀。 |
| pre_segmented_query | 否 | 如果您希望自己将搜索查询拆分为以空格分隔的单词,请将此参数设置为 true。设置为 true时,我们将仅按空格拆分搜索查询,而不是使用语言环境感知的内置分词器。 默认值: false |
| preset | 否 | 要用于此搜索的预设[26]的名称。 预设允许您保存一组搜索参数并在搜索时使用单个 preset参数来使用它们。有关预设的更多信息,请阅读此处[27]。 |
| vector_query | 否 | 执行最近邻向量查询。阅读更多关于 向量搜索[28] 的信息。 |
| voice_query | 否 | 转录 base64 编码的语音录音,并使用转录的查询进行搜索。阅读更多关于 语音查询[29] 的信息。 |
过滤参数
筛选参数
| 参数 | 必填 | 描述 |
| filter_by | 否 | 用于优化搜索结果的筛选条件。 一个字段可以与一个或多个值进行匹配。 示例: - country: USA- country: [USA, UK]返回 country为 USA或 UK的文档。 精确与非精确筛选: 要完全匹配字符串字段的整个值,可以使用 :=(精确匹配)运算符。例如: category := Shoe将匹配 category设置为 Shoe的文档,而不匹配 category字段设置为 Shoe Rack的文档。 使用 :(非精确匹配)运算符将对字段进行词级部分匹配,而不考虑词元位置(通常更快)。例如: category:Shoe将匹配 category为 Shoe、 Shoe Rack或 Outdoor Shoe的记录。 提示:如果某字段的值在所有文档中都没有空格,并且您希望对其进行筛选,建议使用 :运算符以提高性能,因为这将避免进行词元位置检查。 转义逗号: 您还可以使用多个值进行筛选,并使用反引号字符表示字符串字面量: category:= [`Running Shoes, Men`, Sneaker]。 取反: 不等于/取反支持 :!=运算符,例如 author:!=JK Rowling或 id:!=[id1, id2]。您也可以对多个值进行取反: author:!=[JK Rowling, Gilbert Patten]。 要在筛选过程中排除包含特定字符串的结果,可以使用 artist:! Jackson来排除所有 artist字段值包含 jackson的文档。 数字筛选: 使用范围运算符 [min..max]或简单比较运算符 >, >=<, <=, =,可以对数值字段进行筛选,以获取在最小值和最大值之间的文档。 您可以在数值字段模式中启用 "range_index": true以进行快速范围查询(但这将消耗额外的内存)。 示例: - num_employees:[10..100]- num_employees:<40- num_employees:[10..100,40](筛选值在 10 到 100 之间或恰好为 40 的文档)。 多条件筛选: 您可以使用 &&运算符分隔多个条件。 示例: - num_employees:>100 && country: [USA, UK]- categories:=Shoes && categories:=Outdoor要在不同字段(例如:颜色为蓝色或类别为鞋子)之间进行 OR 操作,您可以使用 ` |
| enable_lazy_filter | 否 | 逐步/惰性地应用筛选操作。当您可能在大型值上进行筛选,但查询中的词元预计会匹配非常少的文档时,将此设置为 true。默认值: false。 |
分页参数
| 参数 | 必填 | 描述 |
| page | 否 | 获取指定页码的结果。 页码从 1开始计数。 默认值: 1 |
| per_page | 否 | 获取的结果数量。 当使用 group_by时, per_page指每页获取的_组_数量,以正确维护分页。 默认值: 10注意:每页最多只能获取 250 条结果(使用 group_by时为结果组)。 |
| offset | 否 | 标识返回结果集中起始点的位置。可以作为 page参数的替代方案使用。 |
| limit | 否 | 获取的结果数量。可以作为 per_page参数的替代方案使用。默认值: 10。 |
分组参数
| 参数 | 必填 | 描述 |
| group_by | 否 | 通过指定一个或多个 group_by字段,可以将搜索结果聚合为组或桶。多个字段用逗号分隔。 注意:要对特定字段进行分组,该字段必须是分面字段。 例如: group_by=country,company_name |
| group_limit | 否 | 每个组返回的最大结果数。如果 group_limit设置为 K,则响应中每组只返回前 K 个结果。 默认值: 3 |
| group_missing_values | 否 | 将此参数设置为 true会将 group_by字段中具有 null值的所有文档归入一个组。设置为 false则会使 group_by字段中具有 null值的每个文档不与其他文档分组。 默认值: true |
总结
Typesense 是一个功能强大且易于使用的开源搜索引擎,适合那些需要高性能搜索功能的开发者。通过上述安装步骤,你应该能够轻松地在自己的环境中运行 Typesense 并体验其强大的搜索功能。无论你是在开发一个小型的应用还是一个需要处理大量数据的平台,Typesense 都能为你提供高效且稳定的搜索解决方案。
欢迎关注我的公众号“Hadoop学习之路”,原创技术文章第一时间推送。

引用链接
[1]
点击跳转: https://typesense.org/typesense-vs-algolia-vs-elasticsearch-vs-meilisearch/[2]
作用域搜索 API 密钥: https://typesense.org/docs/26.0/api/api-keys.html#generate-scoped-search-key[3]
Typesense 官方发布页面: https://github.com/typesense/typesense/releases[4]
查询: ./search.md#query-parameters[5]
过滤: ./search.md#filter-results[6]
分面: ./search.md#facet-results[7]
分组: ./search.md#group-results[8]
排序: ./search.md#sort-results[9]
type
: #field-types[10]
facet_by
搜索参数: ./search.md#facet-results[11]
Snowball词干提取器: https://snowballstem.org/[12]
自动架构检测: #with-auto-schema-detection[13]
排除某些字段的索引: #indexing-all-but-some-fields[14]
本指南文章: ../../guide/tips-for-searching-common-types-of-data.md[15]
本指南文章: ../../guide/tips-for-searching-common-types-of-data.md[16]
max_candidates
: ./search.md#ranking-and-sorting-parameters[17]
此处: https://typesense.org/docs/26.0/api/geosearch.html[18]
此处: https://typesense.org/docs/26.0/api/geosearch.html[19]
此处: https://typesense.org/docs/26.0/api/collections.html#indexing-nested-fields[20]
此处: https://typesense.org/docs/26.0/api/collections.html#indexing-nested-fields[21]
图像搜索: https://typesense.org/docs/26.0/api/image-search.html[22]
自动模式检测: https://typesense.org/docs/26.0/api/collections.html#with-auto-schema-detection[23]
文档添加到: https://typesense.org/docs/26.0/api/documents.html#index-a-single-document[24]
“集合别名: https://typesense.org/docs/26.0/api/collection-alias.html[25]
此处: ./collections.md#indexing-nested-fields[26]
预设: #presets[27]
此处: #presets[28]
向量搜索: ./vector-search.md[29]
语音查询: ./voice-search-query.md




