前言

ES 最重要的能力莫过于通过分词能力,实现全文搜索和模糊查询了,要想搭建一个强大的搜索引擎,分词是关键,下面会剖析下 ES 的分词规则以及使用案例,带你从零入门 ES 搜索。

当然前提字段类型必须是 text,分词才会生效。

分词器有哪些类型

1. standard

  • 描述:这是Elasticsearch的默认分词器,基于Unicode文本分割算法,将文本分割成单词。
  • 适用场景:适用于大多数西方语言。

2. keyword

  • 描述:将整个输入作为一个单独的标记(token)。
  • 适用场景:适用于不需要分词的字段,如ID、电子邮件地址等。

3. whitespace

  • 描述:仅根据空格来分割文本。
  • 适用场景:适用于那些主要通过空格分隔单词的语言或特定格式的文本。

4. pattern

  • 描述:使用正则表达式来定义如何分割文本。
  • 适用场景:适用于需要自定义分割规则的复杂场景。

5. uax_url_email

  • 描述:专门用于处理包含电子邮件地址和URL的文本,能够识别并正确分割这些特殊结构。
  • 适用场景:适用于需要解析电子邮件和URL的场景。

6. path_hierarchy

  • 描述:将路径字符串(如文件系统路径)分解成层次结构的标记。
  • 适用场景:适用于需要按路径层级搜索的场景,如文件管理系统。

7. ngram

  • 描述:生成输入文本的所有可能的n-gram序列作为标记。
  • 适用场景:适用于需要模糊匹配或全文搜索的场景,特别是亚洲语言的处理。

8. edge_ngram

  • 对应类org.apache.lucene.analysis.ngram.EdgeNGramTokenizerFactory
  • 描述:类似于N-gram分词器,但只生成从文本开头开始的n-gram序列。
  • 适用场景:适用于自动补全和建议功能。

9. icu_tokenizer

  • 对应类com.ibm.icu.lucene.tokenizer.ICUTokenizerFactory
  • 描述:使用ICU库进行分词,支持Unicode标准和各种语言的特性。
  • 适用场景:适用于需要国际化支持的多语言环境。

10. kstem

  • 对应类org.apache.lucene.analysis.en.KStemTokenizerFactory
  • 描述:Krovetz Stemming算法的实现,主要用于英文文本。
  • 适用场景:适用于需要英文词干提取的场景。

11. mmseg

  • 对应类org.elasticsearch.index.analysis.MMSEGTokenizerFactory
  • 描述:用于中文分词的MMSEG算法实现。
  • 适用场景:适用于中文文本的分词。

12. smartcn

  • 对应类org.elasticsearch.index.analysis.SmartCnTokenizerFactory
  • 描述:Smart Chinese Analysis Plugin提供的中文分词器。
  • 适用场景:适用于中文文本的分词。

过滤器(Filters)

除了分词器之外,Elasticsearch还提供了多种过滤器来进一步处理分词结果:

  • 小写过滤器(lowercase):将所有标记转换为小写。
  • 停用词过滤器(stop):移除常见的无意义词汇(如“and”、“is”、“the”等)。
  • 同义词过滤器(synonym):将标记替换为其同义词。
  • 词干提取过滤器(stemmer):将单词还原为其词根形式。
  • 字符映射过滤器(mapping):根据预定义的映射规则替换或删除标记。

在实际应用中,通常会组合使用分词器和过滤器来达到最佳的分词效果。例如,一个常见的组合是使用标准分词器配合小写过滤器和停用词过滤器,以实现基本的文本处理需求。

示例配置

使用自定义分词器

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "custom_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "stop"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "custom_analyzer"
      }
    }
  }
}

在这个示例中,custom_analyzer使用了标准分词器,并通过小写和停用词过滤器对分词结果进行了进一步处理。

当然也可以直接指定分词器

PUT /my_index
{
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "standard"
      }
    }
  }
}

如果索引已经存在,可以使用以下方式来更新字段的分词器:

PUT /my_index/_mapping
{
  "properties": {
    "content": {
      "type": "text",
      "analyzer": "custom_analyzer"
    }
  }
}

请注意,更新映射并不会改变已经索引的数据,只会影响新添加的数据。

完整示例

创建索引并指定分词器,使用PUT请求来创建一个新的索引,并在索引的设置中定义分词器

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "custom_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "stop"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "custom_analyzer"
      }
    }
  }
}

验证分词器

可以使用 _analyze API 来验证分词器的工作效果:

POST /my_index/_analyze
{
  "analyzer": "custom_analyzer",
  "text": "This is a test sentence."
}

这个请求会返回分词后的结果,帮助你确认分词器是否按预期工作。

更新现有索引的分词器

如果你需要更新现有索引的分词器,可以使用 _update_by_query API 来重新索引数据,或者创建一个新的索引并重新索引数据。

重新索引数据

PUT /my_index_v2
{
  "settings": {
    "analysis": {
      "analyzer": {
        "custom_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "stop"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "custom_analyzer"
      }
    }
  }
}

使用 _reindex API 将数据从旧索引复制到新索引:

POST /_reindex
{
  "source": {
    "index": "my_index"
  },
  "dest": {
    "index": "my_index_v2"
  }
}

完成后,你可以删除旧索引并重命名新索引:

DELETE /my_index
POST /_aliases
{
  "actions": [
    { "remove": { "index": "my_index_v2", "alias": "my_index" } },
    { "add": { "index": "my_index_v2", "alias": "my_index" } }
  ]
}

分词器的性能如何优化

Elasticsearch 分词器的性能优化可以从多个方面进行,以下是一些关键的优化策略:

1. 选择合适的分词器

  • 根据数据的特点选择最合适的分词器。例如,对于中文文本,使用 IK 分词器或者结巴分词器会比标准分词器更有效。

2. 减少不必要的分词

  • 如果字段不需要全文搜索功能,可以将其设置为 keyword 类型,避免不必要的分词操作。

3. 使用缓存

  • Elasticsearch 会对频繁查询的结果进行缓存,确保索引和搜索的缓存设置得当可以显著提高性能。

4. 合理设置分片和副本

  • 分片的数量会影响索引和搜索的速度,需要根据数据量和查询负载合理设置。
  • 副本可以提高查询的并发能力,但也会增加索引的时间和存储需求。

5. 优化索引设置

  • 调整 refresh_interval 来平衡数据的实时性和索引的性能。
  • 使用 index.number_of_replicas 和 index.number_of_shards 来优化索引的读写性能。

6. 使用批量操作

  • 批量索引(Bulk API)可以显著提高索引速度,尽量减少单个文档的索引操作。

7. 避免深分页

  • 深分页(Deep Pagination)会导致 Elasticsearch 性能下降,尽量避免使用 from 和 size 进行大量数据的翻页。

8. 使用合适的硬件资源

  • 确保 Elasticsearch 集群有足够的内存、CPU 和磁盘I/O资源。

9. 监控和调优

  • 使用 Elasticsearch 的监控工具,如 Kibana 或 Marvel,来监控集群的健康状况和性能指标。
  • 根据监控结果进行针对性的调优。

10. 索引模板和映射优化

  • 合理设计索引模板和字段映射,避免不必要的复杂性和冗余。

11. 使用更快的存储

  • 使用 SSD 而不是 HDD 可以提高索引和搜索的速度。

12. 优化查询语句

  • 编写高效的查询语句,避免使用低效的全文搜索和复杂的聚合操作。

13. 定期重建索引

  • 随着数据的增长和变化,定期重建索引可以帮助维持搜索性能。

14. 使用索引别名

  • 使用索引别名可以简化索引管理和提高搜索性能。

15. 考虑使用 Elasticsearch 的最新版本

  • 新版本通常会包含性能改进和优化,确保使用的是最新的稳定版本。