31、深入理解 Elasticsearch 中的词级搜索

深入理解 Elasticsearch 中的词级搜索

1. 词级搜索概述

词级搜索是一种结构化搜索,旨在处理结构化数据,如数字、日期、IP 地址、枚举、关键字类型等。其查询返回的是精确匹配的结果,不关心结果的匹配程度,只关注是否能找到符合查询条件的数据,因此不会为结果关联相关性得分。这种搜索类似于数据库的 WHERE 子句,结果是二元的:满足条件则返回查询结果,不满足则无结果返回。

词级查询的一个重要特点是不进行分析(与全文查询不同)。搜索词直接与倒排索引中存储的词进行匹配,无需应用分析器来匹配索引模式。这意味着搜索词必须与倒排索引中索引的字段完全匹配。

例如,使用词级查询在标题字段中搜索 “Java” 时,可能无法匹配到文档。因为在索引过程中,若使用标准分析器,“Java” 会被转换为小写的 “java” 并插入倒排索引。而词级查询不进行分析,引擎会尝试将搜索词 “Java” 与倒排索引中的 “java” 进行匹配,从而导致匹配失败。若使用关键字类型,则可以返回相同的查询(使用大写的 “Java”)。

词级查询适用于关键字搜索,而不适用于文本字段搜索。因为关键字字段在索引过程中不进行分析,直接添加到倒排索引中。类似地,数值、布尔值、范围等也不进行分析,直接添加到各自的倒排索引中。

以下是一个简单的电影 “The Godfather” 的例子,展示了索引和词级搜索的过程:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    A(文档索引):::process --> B(文本字段分析):::process
    B --> C(分词并小写):::process
    C --> D(插入倒排索引: [the, godfather]):::process
    E(词级搜索):::process --> F(搜索词: The Godfather):::process
    F --> G(与倒排索引匹配):::process
    G --> H(无匹配结果):::process

虽然可以在文本字段上运行词级查询,但不建议在长文本字段上使用。如果文本包含枚举值,如一周的天数、电影证书、性别等,也可以使用词级查询。例如,在索引性别为 “Male” 和 “Female” 时,词级查询必须使用 “male” 和 “female” 才能成功返回结果。

Elasticsearch 提供了一些词级查询,包括 term、terms、IDs、fuzzy、exists、range 等。接下来将详细介绍其中几个重要的查询。

2. 词查询(Term Queries)

词查询用于获取与给定字段精确匹配的文档。该字段不进行分析,而是直接与索引过程中存储在倒排索引中的值进行匹配。

例如,使用电影数据集搜索 R 级电影,可以编写如下词查询:

GET movies/_search
{
  "query": {
    "term": {
      "certificate": "R"
    }
  }
}

上述查询中,查询名称 “term” 表明正在执行词级搜索。对象期望指定字段(这里是 “certificate”)和搜索值。需要注意的是,“certificate” 是关键字数据类型,在索引过程中,值 “R” 未经过任何分析器处理(实际上是关键字分析器,不会改变大小写),因此按原样存储。

运行此查询将返回所有 R 级电影(在示例数据集中有 14 部 R 级电影),结果会封装在返回的 JSON 响应中。

2.1 文本字段上的词查询

如果将查询中的评级值从 “R” 改为小写的 “r”,查询将不会返回任何结果。原因在于,“certificate” 字段是关键字类型,在索引过程中未经过分析,值 “R” 直接插入倒排索引。而词级查询不进行分析,搜索词 “r” 与倒排索引中的 “R” 不匹配,因此无结果返回。

这表明词查询不适用于文本字段。不过,如果文本字段像枚举或常量一样进行索引,也可以使用词查询。例如,订单状态字段包含 “CREATED”、“CANCELLED”、“FULFILLED” 状态,即使该字段是文本字段,也可以使用词查询。

2.2 示例:在电影标题上应用词查询

使用词查询在电影标题字段 “title” 中搜索 “The Godfather”,代码如下:

GET movies/_search
{
  "query": {
    "term": {
      "title": "The Godfather"
    }
  }
}

运行此代码不会返回任何结果。因为 “title” 字段是文本字段,在搜索前经过了分析过程,并存储在索引中。“The Godfather” 被分解为标记并以小写标记 [“the”, “godfather”] 的形式存储在倒排索引中。而词查询不分析搜索词,直接将 “The Godfather” 与倒排索引进行比较,导致匹配失败。

即使将查询改为 “the godfather” 也不会返回结果,因为 “the godfather” 不是倒排索引中的单个词。但搜索 “godfather” 会返回结果,因为 “godfather” 在数据索引过程中被分析并插入倒排索引,从而找到匹配项。

2.3 简化的词级查询

前面看到的词查询是简化版本。下面是完整版本的查询语法:

GET movies/_search
{
  "query": {
    "term": {
      "certificate": {
        "value": "R",
        "boost": 2
      }
    }
  }
}

完整版本中,“certificate” 字段期望一个包含值和其他参数的对象。值 “R” 在简化版本中与字段处于同一级别,而在完整版本中位于字段的下一级。封闭对象还可以包含其他属性,如 “boost”。

完整版本可以为查询添加更多功能,而简化版本则更简洁,适用于简单且无需进一步调整的查询。

3. 多词查询(Terms Queries)

多词查询用于在单个字段上搜索多个条件。可以提供该字段的所有可能值,让搜索在这些值上进行。

例如,要搜索具有多个内容评级(如 PG - 13、R)的所有电影,可以使用多词查询:

GET movies/_search
{
  "query": {
    "terms": {
      "certificate": ["PG-13", "R"]
    }
  }
}

多词查询期望一个搜索词列表,以数组形式传递给 “terms” 对象。数组中的值将逐个与现有文档进行匹配,每个词都进行精确匹配。上述查询将搜索 “certificate” 字段中评级为 PG - 13 和 R 的所有电影,结果文档将是所有 PG - 13 和 R 级电影的组合。

数组中设置的词有数量限制,最多为 65,536 个词。如果需要修改此限制(增加或减少),可以使用索引的动态属性设置 “index.max_terms_count” 来更改限制。以下查询将 “max_terms_count” 设置为 10:

PUT movies/_settings
{
  "index": {
    "max_terms_count": 10
  }
}

此设置将限制用户在 “terms” 数组中设置不超过 10 个值。这是索引的动态设置,可以在实时索引上随时更改。

3.1 多词查找查询(Terms Lookup)

多词查找查询是多词查询的一种变体,它通过读取现有文档的字段值来设置查询词,而不是直接指定。

以下是一个示例,首先创建一个名为 “classic_movies” 的索引,包含两个属性:“title” 和 “director”:

PUT classic_movies
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "director": {
        "type": "keyword"
      }
    }
  }
}

然后向该索引中插入几部电影:

PUT classic_movies/_doc/1
{
  "title": "Jaws",
  "director": "Steven Spielberg"
}

PUT classic_movies/_doc/2
{
  "title": "Jaws II",
  "director": "Jeannot Szwarc"
}

PUT classic_movies/_doc/3
{
  "title": "Ready Player One",
  "director": "Steven Spielberg"
}

现在,假设要获取所有由 “Steven Spielberg” 执导的电影,但不想预先构建多词查询的词列表,而是让查询从文档中获取词的值。可以使用以下多词查找查询:

GET classic_movies/_search
{
  "query": {
    "terms": {
      "director": {
        "index": "classic_movies",
        "id": "3",
        "path": "director"
      }
    }
  }
}

上述代码中,创建了一个多词查询,以 “director” 为字段进行搜索。通常的多词查询会提供一个包含所有名称的数组,而这里让查询从 ID 为 3 的文档中查找 “director” 字段的值。该文档应从 “classic_movies” 索引中获取,要获取值的字段为 “director”。运行此查询将返回由 “Steven Spielberg” 执导的两部电影。

多词查找查询有助于根据从另一个文档获取的值构建查询,而不是在查询中直接传递一组值。在构建查询词时具有更大的灵活性,可以轻松替换索引以从其他文档中获取词。

4. ID 查询(IDs Queries)

有时,我们可能需要从 Elasticsearch 中获取一组特定 ID 的文档。ID 查询可以根据给定的一组文档 ID 来获取匹配的文档,这是一种一次性获取文档的简单方法。

以下是使用文档 ID 列表检索一些文档的示例:

GET /_search
{
  "query": {
    "ids": {
      "values": ["1", "2", "3"]
    }
  }
}

上述查询将返回 ID 为 1、2、3 的文档。

综上所述,词级搜索在处理结构化数据时非常有用,不同的词级查询(词查询、多词查询、ID 查询等)适用于不同的场景。在实际应用中,需要根据数据的特点和查询需求选择合适的查询类型。同时,要注意词级查询不进行分析的特点,避免在文本字段上使用词级查询时出现匹配失败的情况。

5. 模糊查询(Fuzzy Queries)

模糊查询允许在搜索时考虑一定的拼写错误或近似匹配。当我们不确定搜索词的精确拼写时,模糊查询可以帮助我们找到可能相关的结果。

模糊查询的原理是基于编辑距离(Levenshtein 距离),即通过计算一个字符串转换为另一个字符串所需的最少编辑操作(插入、删除、替换)次数来确定两个字符串的相似度。

以下是一个模糊查询的示例,假设我们要在电影标题中搜索可能拼写错误的 “Gofather”,希望找到近似的 “Godfather”:

GET movies/_search
{
  "query": {
    "fuzzy": {
      "title": {
        "value": "Gofather",
        "fuzziness": 1
      }
    }
  }
}

在上述代码中:
- "value" 指定了搜索词 “Gofather”。
- "fuzziness" 指定了允许的最大编辑距离,这里设置为 1,表示搜索词与索引中的词最多允许有 1 个编辑操作的差异。

模糊查询的优缺点如下:
|优点|缺点|
| ---- | ---- |
|可以处理拼写错误,提高搜索的灵活性|计算编辑距离会增加查询的复杂度和时间开销,尤其是在数据量较大时|
|能够找到近似匹配的结果,提供更广泛的搜索范围|模糊度设置不当可能会导致返回过多不相关的结果|

6. 存在查询(Exists Queries)

存在查询用于查找包含指定字段的文档。当我们只关心某个字段是否存在,而不关心该字段的值时,可以使用存在查询。

以下是一个存在查询的示例,查找包含 “director” 字段的电影文档:

GET movies/_search
{
  "query": {
    "exists": {
      "field": "director"
    }
  }
}

在上述代码中, "field" 指定了要检查的字段 “director”。运行此查询将返回所有包含 “director” 字段的电影文档。

存在查询的应用场景包括:
- 数据完整性检查:检查文档是否包含必要的字段。
- 筛选数据:只获取包含特定字段的文档。

7. 范围查询(Range Queries)

范围查询用于查找指定字段值在某个范围内的文档。范围可以是数值范围、日期范围等。

以下是一个数值范围查询的示例,查找评分在 7 到 9 之间的电影:

GET movies/_search
{
  "query": {
    "range": {
      "rating": {
        "gte": 7,
        "lte": 9
      }
    }
  }
}

在上述代码中:
- "gte" 表示大于等于(greater than or equal to)。
- "lte" 表示小于等于(less than or equal to)。

范围查询还可以用于日期范围,例如查找在 2020 年到 2022 年之间上映的电影:

GET movies/_search
{
  "query": {
    "range": {
      "release_date": {
        "gte": "2020-01-01",
        "lte": "2022-12-31"
      }
    }
  }
}

范围查询的操作符及含义如下表所示:
|操作符|含义|
| ---- | ---- |
| gt |大于(greater than)|
| gte |大于等于(greater than or equal to)|
| lt |小于(less than)|
| lte |小于等于(less than or equal to)|

8. 词级搜索的性能优化

在使用词级搜索时,为了提高查询性能,可以考虑以下几点:
- 缓存查询 :词级查询可以被服务器缓存,对于经常运行的相同查询,缓存可以显著提高性能。例如,对于一些固定的搜索条件,可以重复使用缓存的查询结果。
- 选择合适的数据类型 :确保使用合适的数据类型进行索引,如关键字类型用于不需要分析的字段,数值类型用于数值数据等。这样可以避免不必要的分析过程,提高查询效率。
- 控制查询复杂度 :避免在一个查询中使用过多的条件或复杂的逻辑。复杂的查询可能会增加服务器的负担,降低查询性能。例如,尽量将复杂的查询拆分成多个简单的查询。

9. 总结与应用建议

词级搜索在处理结构化数据方面具有独特的优势,不同的词级查询类型适用于不同的场景:
- 词查询(Term Queries) :适用于精确匹配单个字段的值,如关键字、数值、日期等。在使用时要注意字段的分析情况,避免在文本字段上使用词查询导致匹配失败。
- 多词查询(Terms Queries) :用于在单个字段上搜索多个条件,提供了更大的搜索范围。多词查找查询则可以根据现有文档的字段值动态设置查询词,增加了查询的灵活性。
- ID 查询(IDs Queries) :方便一次性获取一组特定 ID 的文档。
- 模糊查询(Fuzzy Queries) :处理拼写错误或近似匹配,提高搜索的灵活性,但要注意模糊度的设置。
- 存在查询(Exists Queries) :用于查找包含指定字段的文档,适用于数据完整性检查和筛选数据。
- 范围查询(Range Queries) :用于查找指定字段值在某个范围内的文档,可处理数值范围和日期范围。

在实际应用中,建议根据数据的特点和查询需求选择合适的查询类型,并结合性能优化策略,以提高搜索效率和准确性。例如,在构建电影搜索系统时,可以根据用户的搜索意图选择不同的词级查询:如果用户输入精确的电影评级,使用词查询;如果用户希望查找多个评级的电影,使用多词查询;如果用户只记得部分电影名称且可能存在拼写错误,使用模糊查询等。

通过合理运用词级搜索的各种查询类型和优化策略,可以更好地满足不同场景下的搜索需求,提升系统的性能和用户体验。

以下是一个简单的词级搜索选择流程图:

graph TD
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    A(搜索需求):::process --> B{精确匹配单个值?}:::process
    B -- 是 --> C(词查询):::process
    B -- 否 --> D{匹配多个值?}:::process
    D -- 是 --> E(多词查询):::process
    D -- 否 --> F{根据 ID 查找?}:::process
    F -- 是 --> G(ID 查询):::process
    F -- 否 --> H{处理拼写错误?}:::process
    H -- 是 --> I(模糊查询):::process
    H -- 否 --> J{检查字段是否存在?}:::process
    J -- 是 --> K(存在查询):::process
    J -- 否 --> L{查找范围值?}:::process
    L -- 是 --> M(范围查询):::process
    L -- 否 --> N(重新评估需求):::process

这个流程图可以帮助我们根据不同的搜索需求快速选择合适的词级查询类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值