在传统数据库中,模糊查询往往难以应对现实中的复杂情况。随着数据量的增加,查询时间显著变慢,功能也相对单一。当搜索时出现错别字或拼音输入时,搜索结果往往不尽如人意。因此,我们需要学习一种强大的搜索框架技术——ElasticSearch。
倒排索引
在传统的模糊搜索中,系统会从表中逐条读取数据,筛选出与模糊搜索条件相关的记录。这种方式通常使用B+树来建立索引,但由于索引与每条数据的内容没有直接关联,导致搜索时需要逐条检索,效率较低。
倒排索引
倒排索引是ElasticSearch等现代搜索引擎的核心技术之一。它引入了文档和词条两个概念:
-
文档:相当于数据库中的一条记录。
-
词条:对文档内容进行分词处理。例如,句子“我是倒排索引”可以通过特定算法拆分为“我”、“是”、“倒排”、“索引”等词条。这些词条可能还包含其他语义相关的词语。
倒排索引的工作原理
假设我们有以下商品数据:
-
"黑玩具"
-
"红玩具"
-
"红书包"
倒排索引会对这些文档进行词条划分,并建立词条与文档编号的映射关系:
-
黑:1
-
玩具:1, 2
-
红:2, 3
-
书包:3
当用户搜索“粉玩具”时,系统会首先对搜索词进行分词处理,得到“粉”和“玩具”两个词条。然后,系统会在倒排索引中查找这些词条对应的文档编号:
-
“粉”没有对应的文档编号。
-
“玩具”对应的文档编号是1和2。
最终,系统会返回文档编号为1和2的结果。由于词条和文档编号都建立了索引,搜索效率非常高。
倒排索引的优势
-
高效检索:通过词条与文档编号的映射,系统可以快速定位相关文档,避免了全表扫描。
-
容错性强:即使搜索词中存在错别字或拼音输入,系统仍然可以通过分词和索引匹配找到相关结果。
-
支持复杂查询:倒排索引支持多种查询方式,如布尔查询、短语查询、范围查询等,能够满足多样化的搜索需求。
IK分词器
ElasticSearch 依靠倒排索引来实现高效的全文搜索,而倒排索引的核心在于分词。对于中文文本,分词尤为重要,因为中文不像英文那样有天然的空格分隔。IK分词器是一个专门为中文设计的分词器,它能够将连续的汉字序列切分成有意义的词语,而不是简单地将每个字分开。这种分词方式不仅保留了语句的完整性,还方便了词条的创建和索引。
在 IK 分词器的配置中,IKAnalyzer.cfg.xml
文件用于对分词行为进行定制化配置。例如,当网络新梗或流行语出现时,传统的分词规则可能无法正确识别这些新词,导致分词结果不准确。通过修改配置文件,我们可以动态地扩展词典或调整分词规则,以适应实际需求。
以下是一个典型的 IKAnalyzer.cfg.xml
配置文件示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<entry key="ext_dict">ext.dic</entry>
<entry key="ext_stopwords"></entry>
</properties>
配置文件解析:
-
ext_dict:用于指定扩展词典文件(如
ext.dic
)。扩展词典允许用户添加新词或专有名词,确保这些词在分词时不会被错误地拆分。例如,网络流行语“内卷”、“躺平”等可以通过扩展词典加入分词器的词库。 -
ext_stopwords:用于指定停用词文件。停用词是指在文本中出现频率较高但对搜索结果影响较小的词,例如“的”、“了”、“是”等。通过配置停用词,可以减少索引的大小,提高搜索效率。
IK分词器的两种模式:
-
ik_smart:智能分词模式,采用粗粒度分词策略。这种模式会将文本切分成较少的词条,适合用于搜索引擎的初步筛选。例如,句子“人工智能技术”在
ik_smart
模式下可能只会分成“人工智能”和“技术”两个词条。 -
ik_max_word:细粒度分词模式,采用最大分词策略。这种模式会尽可能多地将文本切分成不同的词条,适合用于需要高精度的场景。例如,句子“人工智能技术”在
ik_max_word
模式下可能会分成“人工”、“智能”、“人工智能”、“技术”等多个词条。
基本概念
ElasticSearch 的许多概念可以从 MySQL 中延伸理解:
-
索引(Index):相当于 MySQL 中的表(Table),用于存储相关数据。
-
字段(Field):相当于 MySQL 中的列(Column),用于定义数据的属性。
-
文档(Document):相当于 MySQL 中的一行数据(Row),但在 ElasticSearch 中,文档是以 JSON 格式存储的。
-
映射(Mapping):相当于 MySQL 的表结构(Schema),用于定义字段的数据类型和约束。
与 MySQL 不同,ElasticSearch 将每一行数据转换为 JSON 格式存储,这种设计使其在处理非结构化或半结构化数据时更加灵活。此外,ElasticSearch 在应对大规模数据搜索场景时表现出更高的效率,尤其是在全文搜索和复杂查询方面。
然而,MySQL 在数据安全性和事务支持方面更具优势,适合需要强一致性和复杂事务处理的场景。
索引库操作
索引相当于数据库中的表, 映射相当于数据库的表结构, 用来进行数据的约束, 要想进行倒排索引就要设置索引和映射
Mapping映射属性
在数据建模和搜索引擎配置中,映射属性(Mapping)用于定义字段的类型及其处理方式。以下是常见的字段类型及其相关属性:
字段类型
-
字符串类型:
-
Text: 适用于需要分词的文本字段
-
Keyword: 适用于不需要分词的精确文本,如品牌名称、国家代码等。
-
-
数字类型:
-
Integer: 32位整数。
-
Long: 64位整数。
-
Short: 16位整数。
-
Double: 双精度浮点数。
-
Float: 单精度浮点数。
-
Byte: 8位整数。
-
-
布尔类型:
-
Boolean: 表示真或假的值。
-
-
日期类型:
-
Date: 用于存储日期和时间。
-
-
对象类型:
-
Object: 用于嵌套的复杂数据结构。
-
其他属性
-
Index: 确定字段是否被索引。如果设置为
false
,该字段将不会被搜索,但仍可存储在文档中。 -
Properties: 用于定义嵌套字段或子字段的结构。
-
Analyzer: 指定用于分词的分词器类型,适用于
Text
类型的字段。
索引库
索引库的创建与映射设置
- 创建索引库并设置映射
- 在Elasticsearch中,创建索引库的同时可以定义映射(mappings),映射用于描述索引库中文档的结构,包括字段的类型等信息。以下是创建索引库并设置映射的基本操作:
- 使用
PUT
请求来创建索引库并设置映射。例如,创建一个名为my_index
的索引库:- 在上述示例中:
- 对于
title
字段,它被定义为text
类型,并且还定义了一个子字段keyword
,这个子字段也是keyword
类型。这种结构很常见,text
类型适合用于全文搜索,而keyword
类型适合用于精确匹配,比如过滤或者排序。 - 对于
price
字段,它是double
类型,并且设置了"index": false
。这意味着这个字段不会被索引,不能用于搜索,但可以在查询结果中返回该字段的值。
- 对于
- 在上述示例中:
PUT /my_index { "mappings": { "properties": { "title": { "type": "text", "fields": { "keyword": { "type": "keyword" } } }, "price": { "type": "double", "index": false } } } }
获取索引库
- 查询索引库是否存在及基本信息
- 使用
GET
请求可以获取索引库的信息。例如,要获取名为my_index
的索引库的信息:- 如果索引库存在,Elasticsearch会返回该索引库的各种元数据信息,如索引的设置(包括分片数量、副本数量等)、映射等信息。如果索引库不存在,则会返回相应的错误提示。
GET /my_index
- 使用
添加新字段
- 注意事项
- 在Elasticsearch中,如果直接在已经存在的索引库中进行字段修改,可能会导致倒排索引重新排列。当索引库中的数据量非常庞大时,这个操作可能会消耗大量的资源,甚至可能使服务器瘫痪。
- 正确的添加新字段方式
- 要添加新字段,应该使用
PUT
请求针对索引库的_mapping
端点进行操作。例如,要在my_index
索引库中添加一个名为description
的新字段,类型为text
:- 这种方式相对比较安全,并且Elasticsearch会根据新的映射调整索引结构,尽量减少对现有数据和搜索性能的影响。
PUT /my_index/_mapping { "properties": { "description": { "type": "text" } } }
- 要添加新字段,应该使用
删除索引库
- 执行删除操作
- 如果要删除一个名为
my_index
的索引库,可以使用DELETE
请求:- 在执行删除操作之前,需要谨慎考虑,因为一旦索引库被删除,其中所有的数据都将永久丢失,并且无法恢复。
DELETE /my_index
- 如果要删除一个名为
文档操作
新增文档
- 基本语法
- 使用
POST
请求向指定索引库的_doc
路径下添加文档,并可指定文档ID。 - 格式:
POST /索引库名/_doc/文档id
- 示例:
POST /my_index/_doc/1 { "title": "示例文档", "content": "这是新增文档的内容" }
- 若不指定文档ID,Elasticsearch会自动生成一个唯一ID。
POST /my_index/_doc/ { "title": "自动生成ID的文档", "content": "内容" }
- 使用
获取文档
- 基本语法
- 使用
GET
请求通过索引库名、_doc
路径和文档ID来获取特定文档。 - 格式:
GET /索引库名/_doc/{id}
- 示例:
- 若文档存在,将返回该文档的详细信息;若不存在,会返回相应错误提示。
GET /my_index/_doc/1
- 使用
删除文档
- 基本语法
- 使用
DELETE
请求通过索引库名、_doc
路径和文档ID来删除特定文档。 - 格式:
DELETE /索引库名/_doc/{id}
- 示例:
- 成功删除会返回相应成功提示,若文档不存在则返回错误提示。
DELETE /my_index/_doc/1
- 使用
修改文档
一、全量修改
- 操作原理
- 全量修改意味着对文档进行较为彻底的更新操作。如果目标文档在索引库中已经存在,那么会先删除原来的文档,然后再新增一个文档。如果该文档在索引库中原本就不存在,则会直接重新创建这个文档。
- 操作示例
- 假设我们有一个名为“my_index”的索引库,并且要修改或者创建一个id为“1”的文档。其操作如下:
- 使用的API为:PUT /my_index/_doc/1
- 文档内容示例:
{ "title": "新的标题", "content": "这是全新的内容" }
二、局部修改
- 操作原理
- 局部修改主要是针对文档的部分属性进行修改。这种修改方式不需要删除整个文档再重新创建,而是只更新指定的部分属性,从而节省资源和时间。
- 操作示例
- 同样以“my_index”索引库中id为“1”的文档为例,如果只想修改其中“title”这个属性。
- 使用的API为:POST /my_index/_update/1
- 文档内容示例:
{ "doc": { "title": "修改后的标题" } }