一、概念
1、倒排索引
①常规查询
根据id查询数据
②倒排索引
[1]原始数据分词
文档 ID | 文档内容 |
---|---|
1 | 谷歌地图之父跳槽Facebook |
2 | 谷歌地图之父加盟Facebook |
3 | 谷歌地图创始人拉斯离开谷歌加盟Facebook |
4 | 谷歌地图之父跳槽Facebook,与Wave项目取消有关 |
5 | 谷歌地图之父拉斯加盟社交网站Facebook |
文档 ID | 文档内容 |
---|---|
1 | [谷歌] [地图] [之父] [跳槽] [Facebook] |
2 | [谷歌] [地图] [之父] [加盟] [Facebook] |
3 | [谷歌] [地图] [创始] [创始人] [拉斯] [离开] [加盟] [Facebook] |
4 | [谷歌] [地图] [之父] [跳槽] [Facebook] [与] [Wave] [项目] [取消] [有关] |
5 | [谷歌] [地图] [之父] [拉斯] [加盟] [社交] [网站] [Facebook] |
[2]建立索引
单词 ID | 单词 | 关联文档的 ID |
---|---|---|
1 | 谷歌 | 1 2 3 4 5 |
2 | 地图 | 1 2 3 4 5 |
3 | 之父 | 1 2 4 5 |
4 | 跳槽 | 1 4 |
5 | 1 2 3 4 5 | |
6 | 加盟 | 2 3 5 |
7 | 创始 | 3 |
8 | 创始人 | 3 |
9 | 拉斯 | 3 5 |
10 | 离开 | 3 |
11 | 与 | 4 |
12 | Wave | 4 |
13 | 项目 | 4 |
14 | 取消 | 4 |
15 | 有关 | 4 |
16 | 社交 | 5 |
17 | 网站 | 5 |
[3]搜索
用户搜索关键词:拉斯
到索引记录中查询,得知“拉斯”这个词曾出现在id为3和id为5的文档中。
然后就可以根据3和5查询文档本身的数据。
由于这里我们是根据关键词(或关键词分词结果)到索引表中查询得到id,这一步和传统查询根据id查询数据相反,所以叫倒排索引。
ElasticSearch中的索引和MySQL中的索引有啥异同?
相同点:思路一样,都是借助索引提高查询速度。
不同点:ElasticSearch的索引基于分词,是倒排索引结构;MySQL索引是B+Tree结构。
2、数据结构
ElasticSearch | MySQL |
index | 数据库 |
mapping | 表结构 |
表 | |
document | 记录/行 |
field | 字段/列 |
ElasticSearch较高的版本已经废除了type这个概念。
3、数据类型
①是否分词
ElasticSearch中字符串类型有两种情况:
- text:分词
- keyword:不分词
衡量标准:分词之后,分词的结果是否有意义。
例如:深圳,树,/group1/M00/00/02/wKjIgGN8rueEPlm6AAAAACY3A9Q664.png
②是否建立索引
在org.springframework.data.elasticsearch.annotations.Field注解中,通过index属性设置:
- true:建立索引。大部分常规数据,只要是有可能填写到搜索框中的,都建立索引
- false:不建立索引,意思是不根据这个字段建立索引。例如:图片地址
③是否存储
在org.springframework.data.elasticsearch.annotations.Field注解中,通过store属性设置:
- true:存储
- false:不存储
是否存储是指:该字段是否在 _source 之外再做一个额外的存储。这样对于做了存储的字段可以通过 stored_fields 方式获取。
这一点我们作为初学者不必深究,我们只需要知道:即使我们设置了 store 为 false,查询结果中 _source 这里也仍然能够显示该字段。
所以通常是没有影响的。
二、基本操作
1、连接Kibana
友情提示:Kibana在虚拟机启动后,需要等比较长的时间(几分钟)才能访问。
2、基本操作
- index增删改查
- mapping增删改查
- document增删改查
三、DSL语句
1、query关键词语法结构
2、测试数据
- 测试数据
POST db_song/_doc
{
"song_name":"give me an apple",
"song_singer":"tom",
"song_album":"apple pie",
"song_lyrics":"i want to give you an apple",
"song_price":10
}
POST db_song/_doc
{
"song_name":"happy tears",
"song_singer":"jerry",
"song_album":"apple farmer",
"song_lyrics":"i will go to London",
"song_price":20
}
POST db_song/_doc
{
"song_name":"tears of happy",
"song_singer":"jerry",
"song_album":"apple farmer",
"song_lyrics":"i will go to NewYork",
"song_price":30
}
POST db_song/_doc
{
"song_name":"fly me",
"song_singer":"bigger one",
"song_album":"two star",
"song_lyrics":"i put an apple on your table",
"song_price":40
}
3、常规查询
①term
特点:关键词不分词。
举例:关键词是happy tears
搜索结果:未命中,搜索结果列表为空
GET db_song/_search
{
"query": {
"term": {
"song_name": {
"value": "happy tears"
}
}
}
}
说明:happy tears被作为一个整体,和“happy”、“tears”都不匹配,所以没有搜索结果。
②match
特点:关键词分词
举例:关键词是happy tears
搜索结果:匹配到两条记录
GET db_song/_search
{
"query": {
"match": {
"song_name": "happy tears"
}
}
}
③multi_match
特点:关键词分词而且可以匹配多个字段
举例:搜索结果匹配到4条记录
GET db_song/_search
{
"query": {
"multi_match": {
"query": "happy apple",
"fields": [
"song_name",
"song_album",
"song_lyrics"
]
}
}
}
④bool查询
bool 查询有四种子句可以选用,分别是:
- must:指定多个必须满足的条件,各个条件之间是且的关系
- A条件 and B条件
- must_not:指定多个必须不满足的条件
- !A条件 and !B条件
- should:指定多个条件,各个条件之间是或的关系
- A条件 or B条件
- filter:根据指定的条件进行过滤
而上面所提到的条件也就是前面我们刚刚学过的 term、terms、match、match_all、multi_match、match_phrase、range。也就是说:bool 查询就是通过 must、must_not、should、filter 这四个关键词把前面的基本查询方式进一步整合起来,实现更复杂的查询条件。
这很像 SQL 中使用 AND、OR 把 LIKE、BETWEEN AND、IN、NOT IN 等查询条件组合起来。
举例:
GET db_song/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"song_name": "happy"
}
},
{
"match": {
"song_album": "apple"
}
}
],
"filter": {
"range": {
"song_price": {
"gte": 10,
"lte": 20
}
}
}
}
}
}
友情提示:
DSL语句往往能够用很多不同写法完成同样的功能。
所以我们对自己的要求就是:
●借助Kibana提示能够写常规的DSL语句,实现常规功能。
●别人写的DSL语句大致看懂。
⑤聚合查询
[1]概念
聚合查询其实就是分组,相当于SQL语句中的group by。
下面介绍ElasticSearch中关于聚合的两个概念:
- 桶(Bucket):其实就是分组以后的“组”
- 度量(metrics):其实就是SQL语句中的聚合函数
度量方式 | 说明 |
---|---|
Avg Aggregation | 求平均值 |
Max Aggregation | 求最大值 |
Min Aggregation | 求最小值 |
Percentiles Aggregation | 求百分比 |
Stats Aggregation | 同时返回avg、max、min、sum、count等 |
Sum Aggregation | 求和 |
Top hits Aggregation | 求前几 |
Value Count Aggregation | 求总数 |
注意:划分桶时所依据的 filed 不能分词!所以一定要使用 keyword 类型。
[2]操作举例
[3]集合数据类型的聚合
前面我们举例说明的是针对多个document进行聚合(分组)。
此时还有一种特殊情况:某一个字段是集合类型的时候,我们针对这个集合数据进行聚合(分组)操作。
集合数据作为一种比较特殊的类型,在ElasticSearch中类型的名称是:nested
在DSL语句中表现形式是JSON数组的格式。
[4]并列聚合
并列的聚合操作,各自执行各自的聚合逻辑,互不干扰
GET goods/_search
{
"aggs": {
"agg_tm_name_me": {
"terms": {
"field": "tmName",
"size": 10
}
},
"agg_tm_id_me": {
"terms": {
"field": "tmId",
"size": 10
}
},
"agg_cate_3_name_me": {
"terms": {
"field": "category3Name",
"size": 10
}
}
}
}
[5]嵌套聚合
嵌套聚合就是在组内再分组
GET goods/_search
{
"aggs": {
"agg_tm_name_me": {
"terms": {
"field": "tmName",
"size": 10
},
"aggs": {
"agg_tm_id_me": {
"terms": {
"field": "tmId",
"size": 10
}
}
}
}
}
}
4、nested数据类型
①提出问题
先执行下列DSL语句创建测试用的index
PUT my_index/_doc/1
{
"group": "fans",
"user": [
{
"first": "John",
"last": "Smith"
},
{
"first": "Alice",
"last": "White"
}
]
}
然后执行查询
GET my_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"user.first": "Alice"
}
},
{
"match": {
"user.last": "Smith"
}
}
]
}
}
}
查询结果分析:
- 本来must关键字表示“且”的关系,从逻辑上来讲,当前查询条件不应该有查询结果。
- 但是查询结果却查到了
原因是创建my_index的时候,它的默认数据类型是Object。数据在底层存储方式如下:
{"user.first":"John,Alice"}
{"user.last":"Smith,White"}
但实际上我们创建这一数据结构的本意是:
- User1:John Smith
- User2:Alice White
而查询条件中的Alice Smith按照我们的本意来说,不应该存在。
此时对照我们的查询条件,在user.first中匹配到了Alice,在user.last中匹配到了Smith,所以这两个条件都满足。
所以当前document就出现了在了查询结果中。
②修改数据结构
重新创建index
# 删除上面例子中旧的 my_index
DELETE /my_index
# 创建新 my_index 同时指定 mapping
# first 和 last 设置为 keyword 是为了后面测试聚合
PUT my_index
{
"mappings": {
"_doc": {
"properties": {
"user": {
"type": "nested",
"properties": {
"first": {
"type": "keyword"
},
"last": {
"type": "keyword"
}
}
}
}
}
}
}
重新存入数据(这一步和前面是一样的)
PUT my_index/_doc/1
{
"group": "fans",
"user": [
{
"first": "John",
"last": "Smith"
},
{
"first": "Alice",
"last": "White"
}
]
}
③重新查询
此时查询user这个字段,因为它是netsted类型,所以query关键词下面又指定了netsted关键词;path关键词指定了具体要查询的字段:
GET /my_index/_search
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{"match": {"user.first": "Alice"}},
{"match": {"user.last": "Smith"}}
]
}
}
}
}
}
下面语句是能够查询到结果的:
GET /my_index/_search
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{"match": {"user.first": "Alice"}},
{"match": {"user.last": "White"}}
]
}
}
}
}
}
查询结果如下:
说明:上面查询结果会包含John Smith数据,这并不是因为John Smith匹配查询条件,它出现是因为整个document符合查询条件,所以显示了整个document。
④聚合语法
在针对nested类型的字段进行聚合操作时,aggs下的语法也不一样了:
- 聚合方式要选择nested
- 通过path指定聚合字段
- 和nested同级需要再写一个aggs,这里就针对nested类型的数据的某个字段聚合
GET my_index/_search
{
"aggs": {
"fan": {
"nested": {
"path": "user"
},
"aggs": {
"agg_fan": {
"terms": {
"field": "user.first"
}
}
}
}
}
}
执行结果: