数据类型
本文介绍了 Meilisearch 如何处理数据集中不同类型的数据。
此处描述的行为仅与 Meilisearch 的内部流程有关,有助于理解分词器的工作原理。在大多数与 Meilisearch 内部工作无关的实际应用中,文档字段保持不变。
字符串
字符串是 Meilisearch 中索引数据的首要类型。它可以创建搜索内容。字符串按如下所述进行处理。
字符串分词是指将字符串拆分为称为词元的单个词语列表的过程。
字符串传递给分词器,然后被分解成单独的字符串词元。词元是一个词语。
分词
分词依赖于两个主要过程来识别词语并将它们分离成词元:分隔符和字典。
分隔符
分隔符是表示一个词语结束和另一个词语开始的字符。例如,在使用拉丁字母的语言中,词语通常由空格分隔。在日语中,词语边界通常以其他方式表示,例如在词语末尾添加像に
和で
这样的助词。
Meilisearch 中有两种分隔符:软分隔符和硬分隔符。硬分隔符表示重要的上下文切换,例如新句子或新段落。软分隔符仅将一个词语与另一个词语分隔开,但不意味着主题的重大变化。
以下列表展示了一些使用拉丁字母的语言中最常见的分隔符
- 软空格 (距离: 1): 空格,引号,
'-' | '_' | '\'' | ':' | '/' | '\\' | '@' | '"' | '+' | '~' | '=' | '^' | '*' | '#'
- 硬空格 (距离: 8):
'.' | ';' | ',' | '!' | '?' | '(' | ')' | '[' | ']' | '{' | '}'| '|'
有关更多分隔符,包括其他书写系统(如西里尔字母和泰语)中使用的分隔符,请参阅此详尽列表.
字典
对于分词过程,字典是一组应该被视为单个词语的字符列表。字典在识别日语等语言中的词语时特别有用,在这些语言中,词语并不总是由分隔符词元标记。
Meilisearch 为其官方支持的语言提供了一些通用词典。当处理包含许多特定领域术语的文档时,例如法律文件或学术论文,提供 自定义词典 可能会提高搜索结果的相关性。
距离
距离在确定文档是否相关方面起着至关重要的作用,因为 排名规则之一是**邻近性规则**。邻近性规则根据匹配的查询词之间的距离递增对结果进行排序。然后,用软空格分隔的两个词更接近,因此被认为比用硬空格分隔的两个词更**相关**。
在分词过程之后,每个词都会被索引并存储在相应索引的全局词典中。
示例
为了演示字符串是如何通过空格分割的,假设你将以下字符串作为输入
"Bruce Willis,Vin Diesel"
在上面的示例中,Bruce
和 Willis
之间的距离等于**1**。Vin
和 Diesel
之间的距离也是**1**。但是,Willis
和 Vin
之间的距离等于**8**。相同的计算适用于 Bruce
和 Diesel
(10)、Bruce
和 Vin
(9),以及 Willis
和 Diesel
(9)。
让我们看另一个例子。给定两个文档
[
{
"movie_id": "001",
"description": "Bruce.Willis"
},
{
"movie_id": "002",
"description": "Bruce super Willis"
}
]
在对 Bruce Willis
进行查询时,002
将是返回的第一个文档,而 001
将是第二个文档。之所以会发生这种情况,是因为 Bruce
和 Willis
之间的邻近距离在文档 002
中等于**2**,而在文档 001
中等于**8**,因为句号字符 .
是一个硬空格。
数字
数值类型 (integer
、float
) 被转换为人类可读的十进制数字字符串表示。数值类型可以像转换为字符串一样进行搜索。
你可以添加 自定义排名规则,以在文档中具有数值的给定属性上创建升序或降序排序规则。
你还可以创建 过滤器。>
、>=
、<
、<=
和 TO
关系运算符仅适用于数值。
布尔值
布尔值,可以是 true
或者 false
,被接收并转换为小写的人类可读文本 (true
和 false
)。布尔值可以像转换为字符串一样进行搜索。
空
null
类型可以被推送到 Meilisearch 中,但它**不会被考虑进行索引**。
数组
数组是有序的值列表。这些值可以是任何类型:数字、字符串、布尔值、对象,甚至其他数组。
Meilisearch 会展开数组并将它们连接成字符串。非字符串值将根据本文前面部分中描述的方式进行转换。
示例
以下输入
[
[
"Bruce Willis",
"Vin Diesel"
],
"Kung Fu Panda"
]
将被处理,就像所有元素都排列在同一级别一样
"Bruce Willis. Vin Diesel. Kung Fu Panda."
一旦上面的数组被展开,它将根据 字符串示例 中解释的方式进行解析。
对象
当文档字段包含对象时,Meilisearch 会将其展开,并将对象的键和值放到文档本身的根级别。
请记住,此处表示的展开对象是内部流程的中介快照。在搜索时,返回的文档将保留其原始结构。
在下面的示例中,patient_name
键包含一个对象
{
"id": 0,
"patient_name": {
"forename": "Imogen",
"surname": "Temult"
}
}
在索引期间,Meilisearch 使用点表示法来消除嵌套字段
{
"id": 0,
"patient_name.forename": "Imogen",
"patient_name.surname": "Temult"
}
使用点表示法,无论嵌套深度如何,在展开嵌套对象时都不会丢失任何信息。
想象一下,上面的示例文档包含一个额外的对象 address
,它包含家庭和工作地址,每个地址本身都是对象。展开后,文档将如下所示
{
"id": 0,
"patient_name.forename": "Imogen",
"patient_name.surname": "Temult",
"address.home.street": "Largo Isarco, 2",
"address.home.postcode": "20139",
"address.home.city": "Milano",
"address.work.street": "Ca' Corner Della Regina, 2215",
"address.work.postcode": "30135",
"address.work.city": "Venezia"
}
Meilisearch 的内部展开过程还会消除对象数组中的嵌套。在这种情况下,值按键分组。请考虑以下文档
{
"id": 0,
"patient_name": "Imogen Temult",
"appointments": [
{
"date": "2022-01-01",
"doctor": "Jester Lavorre",
"ward": "psychiatry"
},
{
"date": "2019-01-01",
"doctor": "Dorian Storm"
}
]
}
展开后,它将如下所示
{
"id": 0,
"patient_name": "Imogen Temult",
"appointments.date": [
"2022-01-01",
"2019-01-01"
],
"appointments.doctor": [
"Jester Lavorre",
"Dorian Storm"
],
"appointments.ward": [
"psychiatry"
]
}
一旦文档内部的所有对象都已展开,Meilisearch 将继续按照前面部分中描述的方式进行处理。例如,数组将被展开,数值和布尔值将被转换为字符串。
嵌套文档查询和子文档
Meilisearch 没有子文档的概念,也不能执行嵌套文档查询。在前面的示例中,预约的日期和医生之间的关系在展开 appointments
数组时会丢失
…
"appointments.date": [
"2022-01-01",
"2019-01-01"
],
"appointments.doctor": [
"Jester Lavorre",
"Dorian Storm"
],
…
这可能会在搜索期间导致意外行为。以下数据集显示了两位患者及其各自的预约
[
{
"id": 0,
"patient_name": "Imogen Temult",
"appointments": [
{
"date": "2022-01-01",
"doctor": "Jester Lavorre"
}
]
},
{
"id": 1,
"patient_name": "Caleb Widowgast",
"appointments": [
{
"date": "2022-01-01",
"doctor": "Dorian Storm"
},
{
"date": "2023-01-01",
"doctor": "Jester Lavorre"
}
]
}
]
以下查询返回患者 0
和 1
curl \
-X POST 'https://127.0.0.1:7700/indexes/movie_ratings/search' \
-H 'Content-Type: application/json' \
--data-binary '{
"q": "",
"filter": "(appointments.date = 2022-01-01 AND appointments.doctor = 'Jester Lavorre')"
}'
Meilisearch 无法仅返回在 2022-01-01
与 Jester Lavorre
预约的患者。相反,它返回与 Jester Lavorre
预约的患者,以及在 2022-01-01
预约的患者。
解决此限制的最佳方法是重新格式化你的数据。上面的示例可以通过将预约数据合并到一个新的 appointmentsMerged
字段中来解决,这样预约和医生之间的关系就可以保持完整
[
{
"id": 0,
"patient_name": "Imogen Temult",
"appointmentsMerged": [
"2022-01-01 Jester Lavorre"
]
},
{
"id": 1,
"patient_name": "Caleb Widowgast",
"appointmentsMerged": [
"2023-01-01 Jester Lavorre"
"2022-01-01 Dorian Storm"
]
}
]
可能的标记问题
即使它的行为完全符合预期,但在某些情况下,标记过程可能会导致违反直觉的结果,例如
"S.O.S"
"George R. R. Martin"
10,3
对于上面的两个字符串,句号 .
将被视为硬空格。
10,3
将被拆分为两个字符串——10
和 3
——而不是被处理为数值类型。