本文解释了 Meilisearch 如何处理数据集中不同类型的数据。 这里描述的行为仅涉及 Meilisearch 的内部流程,有助于理解分词器的工作原理。对于大多数与 Meilisearch 内部工作无关的实际用途,文档字段保持不变。

字符串

字符串是 Meilisearch 中索引数据的主要类型。它能够创建可供搜索的内容。字符串的处理方式如下所述。 字符串分词是将字符串拆分为称为令牌的单个术语列表的过程。 字符串被传递给分词器,然后被分解成单独的字符串令牌。令牌是一个单词

分词

分词依赖于两个主要过程来识别单词并将其分离为令牌:分隔符和字典。

分隔符

分隔符是指示一个单词结束和另一个单词开始的字符。例如,在使用拉丁字母的语言中,单词通常由空格分隔。在日语中,单词边界通常通过其他方式指示,例如在单词末尾附加“に”和“で”等助词。 Meilisearch 中有两种分隔符:软分隔符和硬分隔符。硬分隔符表示重要的上下文切换,例如新句子或段落。软分隔符只将一个单词与另一个单词分隔开,但并不意味着主题的重大改变。 下面的列表列出了一些使用拉丁字母的语言中最常见的分隔符:
  • 软空格(距离:1):空格、引号、'-' | '_' | '\'' | ':' | '/' | '\\' | '@' | '"' | '+' | '~' | '=' | '^' | '*' | '#'
  • 硬空格(距离:8):'.' | ';' | ',' | '!' | '?' | '(' | ')' | '[' | ']' | '{' | '}'| '|'
有关更多分隔符,包括西里尔字母和泰语等其他书写系统中使用的分隔符,请查阅此详尽列表

字典

对于分词过程,字典是字符组的列表,应将其视为单个术语。字典在识别日语等语言中的单词时特别有用,因为这些语言中的单词并不总是通过分隔符标记。 Meilisearch 为其官方支持的语言提供了许多通用字典。当处理包含许多领域特定术语的文档时,例如法律文档或学术论文,提供自定义字典可能会提高搜索结果的相关性。

距离

距离在确定文档是否相关方面起着至关重要的作用,因为其中一个排名规则是邻近规则。邻近规则按匹配查询术语之间距离的增加来对结果进行排序。因此,由软空格分隔的两个单词比由硬空格分隔的两个单词更接近,因此被认为是更相关的。 分词过程结束后,每个单词都会被索引并存储在相应索引的全局字典中。

示例

为了演示字符串如何按空格分隔,假设您有以下字符串作为输入
"Bruce Willis,Vin Diesel"
在上面的示例中,BruceWillis 之间的距离等于 1VinDiesel 之间的距离也等于 1。然而,WillisVin 之间的距离等于 8。同样的计算也适用于 BruceDiesel (10)、BruceVin (9),以及 WillisDiesel (9)。 让我们看另一个例子。给定两个文档:
[
  {
    "movie_id": "001",
    "description": "Bruce.Willis"
  },
  {
    "movie_id": "002",
    "description": "Bruce super Willis"
  }
]
当查询Bruce Willis时,002将是第一个返回的文档,001将是第二个。发生这种情况是因为文档002BruceWillis之间的邻近距离等于2,而文档001BruceWillis之间的距离等于8,因为句号字符.是一个硬空格。

数字

数值类型(integer, float)会被转换为人类可读的十进制数字字符串表示。数值类型可以像转换为字符串一样进行搜索。 您可以添加自定义排名规则,以在文档中具有数值的给定属性上创建升序或降序排序规则。 您还可以创建过滤器>, >=, <, <=TO 关系运算符仅适用于数值。

布尔值

布尔值,即truefalse,会被接收并转换为小写的人类可读文本(truefalse)。布尔值可以像转换为字符串一样进行搜索。

null

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"
      }
    ]
  }
]
以下查询返回患者 01
curl \
  -X POST 'MEILISEARCH_URL/indexes/clinic_patients/search' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "",
    "filter": "(appointments.date = 2022-01-01 AND appointments.doctor = 'Jester Lavorre')"
  }'
Meilisearch 无法仅返回在 2022-01-01Jester 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将被拆分成两个字符串——103——而不是作为数字类型处理。
© . This site is unofficial and not affiliated with Meilisearch.