# 常见数据类型搜索技巧
本文将介绍如何索引和搜索以下类型的数据:
# 型号/零件编号/SKU
假设您有一个包含产品标识符(型号、零件编号或SKU)的文档,其中混合了字母数字字符和特殊字符:
{
"title": "控制臂衬套套件",
"part_number": "K83913.39F29.59444AT"
//...
}
现在您希望该产品在以下任何搜索词中都能显示在搜索结果中:
K83913
83913
39F29
59444AT
59444
9444AT
K83913.39F29
39F29.59444
# 默认行为
默认情况下,Typesense在索引和搜索字段时会移除特殊字符。
因此K83913.39F29.59444AT
会被索引为K8391339F2959444AT
。
默认情况下,Typesense执行前缀搜索(Prefix Search),这意味着它只搜索字符串_开头_与搜索词匹配的记录。
因此搜索出现在K83913.39F29.59444AT
中间的39F29
或F29
不会返回该记录。
但搜索K83913
或K83913.39
或K83913.39F29.59444
或K83913.39
会返回该记录。
# 精细调优
我们需要做的第一个调整是告诉 Typesense 使用 .
(句点)作为产品标识符的分隔符。
这样 K83913.39F29.59444AT
就会被索引为三个独立的词元(单词):K83913
、39F29
和 59444AT
。
现在当你搜索 39F29
或 5944
时,就能返回产品 K83913.39F29.59444AT
。
你可以通过在 schema 参数 中设置 token_separators
,在 创建集合 时实现:
{
"name": "products",
"fields": [
{"name": "title", "type": "string"},
{"name": "part_number", "type": "string"}
],
"token_separators": ["."]
}
字段级控制
从 Typesense v28.0
开始,你可以在字段级别配置 token_separators
和 symbols_to_index
,这些设置会覆盖集合级别的配置。
示例:
{
"name": "products",
"fields": [
{
"name": "part_number",
"type": "string",
"token_separators": ["-", "."],
"symbols_to_index": ["_"]
}
]
}
但我们仍然需要处理搜索 83913
或 9444AT
这类出现在字符串中间的情况。
针对这个问题,我们有两个解决方案:
使用
v0.23.0
版本新增的infix
搜索功能:https://github.com/typesense/typesense/issues/393#issuecomment-1065367947 (opens new window)
注意:对于长字符串,这可能是计算密集型的操作。 如果你发现特定用例中 CPU 使用率升高,可以考虑使用下面的方案。
根据用户的预期搜索方式预先拆分产品标识符:
{ "title": "Control Arm Bushing Kit", "part_number": [ "K83913.39F29.59444AT", "83913.39F29.59444AT", "3913.39F29.59444AT", "913.39F29.59444AT", "13.39F29.59444AT", "3.39F29.59444AT", "9F29.59444AT", "F29.59444AT", "29.59444AT", "9.59444AT", "9444AT", "444AT", "44AT", "4AT", "AT" ] //... }
当结合使用
token_separators
时,你将能够搜索我们上面讨论的所有模式。
# 电话号码
假设我们有如下格式的电话号码:+1 (234) 567-8901
,
我们希望用户能够使用以下任意一种模式来检索到这条记录:
8901
567-8901
567 8901
5678901
234-567-8901
(234) 567-8901
(234)567-8901
1-234-567-8901
+12345678901
12345678901
2345678901
+1(234)567-8901
# 默认行为
默认情况下,Typesense 会移除所有特殊字符,并通过空格分割标记(单词),因此 +1 (234) 567-8901
会被索引为 1
、234
、5678901
。
所以搜索 234
或 5678901
或 234 567-8901
会返回结果,但其他模式则不会返回预期结果。
# 精细调优
我们首先需要告诉 Typesense 使用 (
、)
和 -
作为分隔符,这可以通过在 schema 参数 中设置 token_separators
来实现,具体在 创建集合 时:
{
"name": "users",
"fields": [
{"name": "first_name", "type": "string"},
{"name": "phone_number", "type": "string"}
],
"token_separators": ["(", ")", "-"]
}
这样设置后,+1 (234) 567-8901
将被索引为 1
、234
、567
和 8901
,现在以下搜索都能返回该文档:
8901
567-8901
567 8901
234-567-8901
(234) 567-8901
(234)567-8901
1-234-567-8901
+1(234)567-8901
还需要处理的剩余情况是:
5678901
+12345678901
12345678901
2345678901
为了解决这些问题,你需要在文档中添加这些额外格式作为一个 string[]
数组字段:
{
"name": "users",
"fields": [
{"name": "first_name", "type": "string"},
{"name": "phone_number", "type": "string[]"}
],
"token_separators": ["(", ")", "-"]
}
{
"name": "Tom",
"phone_number": [
"+1 (234) 567-8901",
"12345678901", // 移除所有空格
"2345678901", // 移除所有空格和国家代码
"5678901" // 移除所有空格、国家代码和区号
]
}
现在,搜索以上任意一种模式都能找到这条记录。
# 电子邮件地址处理
假设我们有一个电子邮件地址 contact+docs-example@typesense.org
,我们希望用户能够通过以下任意模式检索到该文档:
contact+docs-example
contact+docs-example@
contact+docs-example@typesense
contact+docs
contact docs
docs example
contact typesense
contact
docs
example
typesense
typesense.org
# 默认行为
默认情况下,Typesense 在索引时会移除所有特殊字符,并且只执行前缀搜索(搜索词必须出现在单词开头),因此 contact+docs-example@typesense.org
会被索引为 contactdocsexampletypesense.org
。
所以带有 ✅ 的搜索词能返回该记录,而带有 ❌ 的则不会:
- ✅
contact+docs-example
- ✅
contact+docs-example@
- ✅
contact+docs-example@typesense
- ✅
contact+docs
- ❌
contact docs
- ❌
docs example
- ❌
contact typesense
- ✅
contact
- ❌
docs
- ❌
example
- ❌
typesense
- ❌
typesense.org
# 精细调优
为了解决上述剩余情况,我们可以在创建集合时,使用token_separators
模式参数:
{
"name": "users",
"fields": [
{"name": "first_name", "type": "string"},
{"name": "email", "type": "string"}
],
"token_separators": ["+", "-", "@", "."]
}
这将使 contact+docs-example@typesense.org
被索引为 contact
、docs
、example
、typesense
和 org
。
现在所有搜索词都能匹配到这条记录:
- ✅
contact+docs-example
- ✅
contact+docs-example@
- ✅
contact+docs-example@typesense
- ✅
contact+docs
- ✅
contact docs
- ✅
docs example
- ✅
contact typesense
- ✅
contact
- ✅
docs
- ✅
example
- ✅
typesense
- ✅
typesense.org
如果你还希望 ample
也能返回这条记录,可以使用 v0.23.0
版本起提供的 infix
搜索功能:
https://github.com/typesense/typesense/issues/393#issuecomment-1065367947 (opens new window)
# 日期/时间处理
Typesense 没有原生的日期/时间数据类型。
因此你需要按照这里的说明将日期和时间转换为 Unix 时间戳。
# 嵌套对象
# 从 Typesense v0.24.0 开始
Typesense v0.24.0 原生支持嵌套对象和对象数组。
要启用嵌套字段,您需要在创建集合时使用 enable_nested_fields
属性,并配合 数据类型 object
或 object[]
:
{
"name": "docs",
"enable_nested_fields": true,
"fields": [
{"name": "person", "type": "object"},
{"name": "details", "type": "object[]"}
]
}
了解更多信息请点击 这里。
# Typesense v0.23.1 及更早版本
Typesense v0.23.1 及更早版本仅支持索引整型、浮点型、字符串、布尔值以及包含这些数据类型的数组字段值。 集合中的字段只能指定这些将被索引的数据类型。
重要补充说明: 您仍然可以向 Typesense 发送包含嵌套对象的字段(只要这些字段未在 schema 中声明)。这些字段不会被索引或类型检查,只会被存储在磁盘上,当文档匹配搜索查询时原样返回。
Typesense 目前明确不支持对嵌套对象或对象数组进行 索引、搜索 或 过滤 操作。我们计划在短期内添加对此功能的支持(参见 #227 (opens new window))。在此之前,您需要将对象和对象数组展平为顶级键,然后再将数据发送到 Typesense。
例如,包含嵌套对象的文档如下:
{
"nested_field": {
"field1": "value1",
"field2": ["value2", "value3", "value4"],
"field3": {
"fieldA": "valueA",
"fieldB": ["valueB", "valueC", "valueD"]
}
}
}
需要展平为:
{
"nested_field.field1": "value1",
"nested_field.field2": ["value2", "value3", "value4"],
"nested_field.field3.fieldA": "valueA",
"nested_field.field3.fieldB": ["valueB", "valueC", "valueD"]
}
然后才能索引到 Typesense 中。
为了简化结果数据的遍历,您可以同时向 Typesense 发送嵌套字段的展平版本和原始版本,在集合 schema 中仅将展平键设置为可索引,并用于搜索/过滤/分面操作。在显示结果时解析数据时,则可以使用嵌套版本。
# 地理坐标
Typesense 支持使用文档中的经纬度数据进行地理搜索(GeoSearch)查询。您可以:
- 过滤给定经纬度半径范围内的文档
- 按与指定经纬度的接近程度排序结果
- 返回边界框内的结果
有关地理搜索查询的更多信息,请参阅:GeoSearch API 参考。
# 长文本处理
如果您需要处理长文本内容,比如长篇期刊文章、网页内容或文字记录等,我们建议您将长文本拆分成较小的"段落",并将每个段落作为单独的文档存储在Typesense中。
这种方式可以提高搜索结果的粒度并增强相关性。因为如果文本过长,文档之间可能会出现大量关键词重叠,导致搜索常见关键词时匹配到过多文章。
# HTML内容处理
如果要搜索HTML内容,您需要在文档中创建一个仅包含纯文本内容(不含HTML标签)的字段,并在query_by
搜索参数中使用该字段。
您仍然可以将原始HTML字段作为未索引字段存储在文档中(只需在schema中不声明该字段),这样当文档匹配时,原始HTML内容仍会被返回。
这里 (opens new window)提供了更多相关背景信息。
# 搜索null
或空值
Typesense原生不支持直接筛选属性值为null
或空的文档。
但您可以通过以下方法实现这一需求。假设您的文档中有一个可选字段tags
可能为null
:
{
"tags": null
}
如果要查找所有tags
为null
的文档,您需要在索引时为每个文档创建一个额外的字段is_tags_null: true | false
:
[
{
"tags": null,
"is_tags_null": true
},
{
"tags": ["tag1", "tag3"],
"is_tags_null": false
}
]
在所有文档中设置好这个字段后,您就可以通过以下查询来查找这些文档:
{
"filter_by": "is_tags_null:true"
}
# URL 或文件路径
假设您有一些包含 URL 或文件路径的文档需要进行搜索,例如:
{"url": "https://url1.com/path1"}
{"url": "https://url2.com/path2"}
{"url": "https://url3.com/path3"}
您希望当用户搜索 url1
或 path1
等内容时,Typesense 能够返回相应结果。
# 默认行为
默认情况下,Typesense 会移除所有特殊字符并将第一个文档索引为 httpsurl1compath1
。
此外,Typesense 执行的是前缀搜索(匹配必须出现在单词开头),因此 url1
或 path1
不会返回任何结果,因为它们出现在索引字符串的中间位置。
# 优化方案
为了解决这个问题并仍然能检索到 url1
或 path1
的结果,您需要在集合模式中的 token_separators
设置里添加 :
、.
和 /
:
{
"name": "pages",
"fields": [
{"name": "title", "type": "string"},
{"name": "url", "type": "string"}
],
"token_separators": [":", "/", "."]
}
这样配置后,URL 将被索引为独立的单词:https
、url1
、com
、path1
。
现在当您搜索 url1
或 path
时,系统会匹配这些独立单词并返回对应文档。
# 其他数据类型
如果您有其他特定类型的数据需要帮助在 Typesense 中进行索引, 请提交 GitHub issue (opens new window) 或加入我们的 Slack 社区 (opens new window) 咨询。