1.写在前面

前面我已经介绍了elasticsearch的环境的搭建还有就是集群的环境的搭建,但是我们从来没有上手过对应的API,今天的博客我们来介绍下elasticsearch的常用的API以及elasticsearch的分词的策略,以及我们怎么安装我们的分词器。废话不多说,直接开始我们的博客吧。

2.常用的API

2.1创建空索引

PUT /king //索引的名称 
{
	"settings": {
		"number_of_shards": "2",   //分片数
		"number_of_replicas": "0",  //副本数
		"write.wait_for_active_shards": 1 //写的活跃数,写的时候只有节点活跃数只有大于这个数,才能写入
	}
}

2.2修改副本数

PUT king/_settings
{
    "number_of_replicas" : "2"
}

2.3删除索引

DELETE /king

2.4插入数据

//指定id
POST /king/_doc/1001
{
  "id":1001,
  "name":"张三",
  "age":20,
  "sex":"男"
}

//不指定id  es帮我们自动生成
POST /king/_doc
{
  "id":1002,
  "name":"三哥",
  "age":20,
  "sex":"男"
}

2.5更新数据

在Elasticsearch中,文档数据是不为修改的,但是可以通过覆盖的方式进行更新

PUT /king/_doc/1001
{
  "id":1009,
  "name":"李四",
  "age":21,
  "sex":"哈哈"
}

2.6局部更新

其实elasticsearch内部对partial update的实际执行和传统的全量替换方式是几乎一样的,其步骤如下:

  1. 内部先获取到对应的document;
  2. 将传递过来的field更新到document的json中(这一步实质上也是一样的);
  3. 将老的document标记为deleted(到一定时候才会物理删除);
  4. 将修改后的新的document创建出来
POST /king/_update/1001
{
  "doc":{
     "age":23
  }
}

替换和更新的不同:替换是每次都会去替换,更新是有新的东西就更新,没有新的修改就不更新,更新比替换的性能好

2.7删除数据

DELETE /king/_doc/1001

2.8根据id搜索数据

GET /king/_doc/6_h43W0BdTjVHQ-cgnv2 //文档的id

2.9搜索全部数据

GET /king/_search    默认最多返回10条数据

POST /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    {
      "属性名": {
        "order": "asc"
      }
    }
  ]
}

返回的结果的情况的解释

结果参数解释
tookElasticsearch运行查询需要多长时间(以毫秒为单位)
timed_out搜索请求是否超时
_shards搜索了多少碎片,并对多少碎片成功、失败或跳过进行了细分。
max_score找到最相关的文档的得分
hits.total.value找到了多少匹配的文档
hits.sort文档的排序位置(当不根据相关性得分排序时)
hits._score文档的相关性评分(在使用match_all时不适用)

2.10关键字搜索数据

GET /king/_search?q=age:23    //查询年龄等于23的

2.11DSL搜索

POST /king/_search
{
  "query" : {
    "match" : {       //查询年龄等于23的
      "age" : 23
    }
  }
}


//查询地址等于mill或者lane
GET /bank/_search
{
  "query": { "match": { "address": "mill lane" } }
}

//查询地址等于(mill lane)的
GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}

//注意:match 中如果加空格,那么会被认为两个单词,包含任意一个单词将被查询到
//match_parase 将忽略空格,将该字符认为一个整体,会在索引中匹配包含这个整体的文档。
POST /king/_search    //查询年龄大于20  并且性别是男的
{
  "query": {
    "bool": {
      "filter": {
        "range": {
            "age": {
              "gt": 20
            }
          }
        },
      "must": {
        "match": {
          "sex": "男"
        }
      }
    }
  }
}   

2.12高亮显示

POST /king/_search   			//这里会分词搜索
{
  "query": {
    "match": {
      "name": "张三"
    }
  },
  "highlight": {
    "fields": {
      "name": {}
    }
  }
}

2.13聚合

这儿我只讲一部分,具体的可以看官网的介绍,具体的地址:https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations.html

avg :平均值
max:最大值
min:最小值
sum:求和

例如:查询平均年龄 (如果不指定size等于0,则还会返回10条数据)

POST /bank/_search
{
  "aggs": {
    "taibai": {   //自定义名字
      "avg": {    //什么类型
        "field": "age"    //那个字段
      }
    }
  },
  "size": 0
}

使用脚本

POST /bank/_search
{
  "aggs": {
    "taibai": {
      "avg": {
        "script": {
          "source": "doc.age.value"
        }
      }
    }
  },
  "size": 0
}
cardinality : 去重统计

例如:

POST /bank/_search
{
  "aggs": {
    "taibai": {
      "cardinality": {
        "field": "age"
      }
    }
  },
  "size": 0
}
extended_stats扩展统计聚合
POST /bank/_search
{
  "aggs": {
    "taibai": {
      "extended_stats": {
        "field": "age"
      }
    }
  },
  "size": 0
}
value_count值计数统计

可以理解为统计个数

terms词聚合

基于某个field,该 field 内的每一个【唯一词元】为一个桶,并计算每个桶内文档个数。默认返回顺序是按照文档个数多少排序。

POST /bank/_search
{
  "aggs": {
    "taibai": {
      "terms": {
        "field": "age"
      }
    }
  },
  "size": 0
}
top_hits最高匹配权值聚合

获取到每组前n条数据,相当于sql 中Top(group by 后取出前n条)。它跟踪聚合中相关性最高的文档

POST /bank/_search
{
  "aggs": {
    "taibai": {
      "terms": {
        "field": "age"
      },
      "aggs": {
        "count": {
          "top_hits": {
            "size": 3
          }
        }
      }
    }
  },
  "size": 0
}
range范围
POST bank/_search
{
  "aggs": {
    "group_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          },
          {
            "from": 30,
            "to": 40
          },
          {
            "from": 40,
            "to": 50
          }
        ]
      }
    }
  },
  "size": 0
}

2.14查询响应

如果使用浏览器工具去查询,返回的json没有格式化,可在后面加参数pretty,返回格式化后的数据

http://192.168.204.209:9200/taibai/_doc/_fiK3W0BdTjVHQ-c0HvY?pretty

2.15指定响应的字段

GET /king/_doc/9_iK3W0BdTjVHQ-czHuE?_source=id,name    //只返回id和name字段

2.16去掉元数据

GET /king/_source/9_iK3W0BdTjVHQ-czHuE

//还可以去掉元数据并且返回指定字段
GET /king/_source/9_iK3W0BdTjVHQ-czHuE?_source=id,name  

2.17判断文档是否存在

HEAD /king/_doc/9_iK3W0BdTjVHQ-czHuE

2.18批量操作

语法实例

POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }

2.19批量查询

如果,某一条数据不存在,不影响整体响应,需要通过found的值进行判断是否查询到数据。

POST /king/_mget
{
"ids" : [ "8fiK3W0BdTjVHQ-cxntK", "9fiK3W0BdTjVHQ-cy3sI" ]
}

2.20批量插入

POST _bulk
{ "create" : { "_index" : "king", "_id" : "3" } }
{"id":2002,"name":"name1","age": 20,"sex": "男"}
{ "create" : { "_index" : "taibai", "_id" : "4" } }
{"id":2003,"name":"name1","age": 20,"sex": "男"}

2.21批量删除

POST _bulk
{ "delete" : { "_index" : "king", "_id" : "8PiK3W0BdTjVHQ-cxHs1" } }
{ "delete" : { "_index" : "king", "_id" : "6vh43W0BdTjVHQ-cHXv8" } }

2.22批量修改

POST _bulk
{ "update" : {"_id" : "4", "_index" : "king"} }
{ "doc" : {"name" : "king"} }
{ "update" : {"_id" : "3", "_index" : "king"} }
{ "doc" : {"name" : "king"} }

2.23分页查询

GET /king/_search?size=1&from=2     //size: 结果数,默认10      from: 跳过开始的结果数,默认0
分页一

浅分页,它的原理很简单,就是查询前20条数据,然后截断前10条,只返回10-20的数据。这样其实白白浪费了前10条的查询

GET /bank/_search
{
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ],
  "size": 1000,
  "from": 0
}
分页二

scroll 深分页,使用scroll,每次只能获取一页的内容,然后会返回一个scroll_id。根据返回的这个scroll_id可以不断地获取下一页的内容,所以scroll并不适用于有跳页的情景。

  1. scroll=5m表示设置scroll_id保留5分钟可用。
  2. 使用scroll必须要将from设置为0。
  3. size决定后面每次调用_search搜索返回的数量。
GET /bank/_search?scroll=5m
{
  "size": 20,
  "from": 0,
  "sort": [
    {
      "_id": {
        "order": "desc"
      }
    }
  ]
}

//会返回一个:
"_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAB9AWTVIyY1pKcmhUT0dBT1FXLU5ueHdDQQ=="
 
//以后调用:
GET _search/scroll
{
  "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAABMIWTVIyY1pKcmhUT0dBT1FXLU5ueHdDQQ==",
  "scroll": "5m"
}

//删除scroll_id
DELETE _search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAABMIWTVIyY1pKcmhUT0dBT1FXLU5ueHdDQQ==

//删除所有scroll_id
DELETE _search/scroll/_all

//注意:根据官方文档的说法,scroll是非常消耗资源的,所以一个建议就是当不需要了scroll数据的时候,尽可能快的把scroll_id显式删除掉。scroll 的方式,官方的建议不用于实时的请求(一般用于数据导出),因为每一个 scroll_id 不仅会占用大量的资源,而且会生成历史快照,对于数据的变更不会反映到快照上。
分页三

search_after 深分页,是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。但是需要注意,因为每一页的数据依赖于上一页最后一条数据,所以无法跳页请求。为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,官方推荐使用 _uid 作为全局唯一值,其实使用业务层的 id 也可以。使用search_after必须要设置from=0。

GET /bank/_search
{
  "size": 20,
  "from": 0,
  "sort": [
    {
      "_id": {
        "order": "desc"
      }
    }
  ]
}

//拿到返回最后一条数据的_id
GET /bank/_search
{
  "size": 20,
  "from": 0,
  "sort": [
    {
      "_id": {
        "order": "desc"
      }
    }
  ],
  "search_after": [
    980
  ]
}

2.24映射

前面我们创建的索引以及插入数据,都是由Elasticsearch进行自动判断类型,有些时候我们是需要进行明确字段类型的,否则,自动判断的类型和实际需求是不相符的。

自动判断的规则如下:

JSON typeField type
Boolean: true or false“boolean”
Whole number: 123“long”
Floating point: 123.45“double”
String, valid date: “2014-09-15”“date”
String: “foo bar”“string”

创建明确类型的索引:

PUT /goods
{
  "settings": {
    "number_of_replicas": 0,
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "long"
      },
      "sn": {
        "type": "keyword"
      },
      "name": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "price": {
        "type": "double"
      },
      "num": {
        "type": "integer"
      },
      "alert_num": {
        "type": "integer"
      },
      "image": {
        "type": "keyword"
      },
      "images": {
        "type": "keyword"
      },
      "weight": {
        "type": "double"
      },
      "create_time": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      },
      "update_time": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      },
      "spu_id": {
        "type": "keyword"
      },
      "category_id": {
        "type": "integer"
      },
      "category_name": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "brand_name": {
        "type": "keyword"
      },
      "spec": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "sale_num": {
        "type": "integer"
      },
      "comment_num": {
        "type": "integer"
      },
      "status": {
        "type": "integer"
      }
    }
  }
}

添加一个字段到现有的映射

PUT /luban/_mapping
{
  "properties": {
    "isold": {      //字段名
      "type": "keyword",  //类型
      "index": false
    }
  }
}

更新字段的映射

除了支持的映射参数外,您不能更改现有字段的映射或字段类型。更改现有字段可能会使已经建立索引的数据无效。

如果您需要更改字段映射,创建具有正确映射一个新的索引和重新索引的数据转换成指数。

重命名字段会使在旧字段名称下已建立索引的数据无效。而是添加一个alias字段以创建备用字段名称。

查看索引的映射

GET /king/_mapping

查看指定字段的映射信息

GET /king/_mapping/field/name

2.25结构化查询

term查询

term 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型):

POST /king/_search
{
  "query" : {
    "term" : {
      "age" : 20
    }
  }
}
terms查询

terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:

POST /taibai/_search
{
  "query" : {
    "terms" : {
      "age" : [20,27]
    }
  }
}
range查询

range 过滤允许我们按照指定范围查找一批数据:

gt :: 大于

gte :: 大于等于

lt :: 小于

lte :: 小于等于

POST /king/_search
{
  "query": {
    "range": {
      "age": {
        "gte": 20,
        "lte": 22
      }
    }
  }
exists查询

exists 查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的 IS_NULL 条件 包含这个字段就返回这条数据

POST /taibai/_search
{
  "query": {
    "exists": {
      "field": "name"
    }
  }
}
match查询

match 查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析 match 一下查询字符;如果用 match 下指定了一个确切值,在遇到数字,日期,布尔值或者 not_analyzed 的字符串时,它将为你搜索你给定的值:

POST /taibai/_search
{
  "query" : {
    "match" : {     
      "name" : "三个小矮人"
    }
  }
}
//match查询会先对搜索词进行分词,分词完毕后再逐个对分词结果进行匹配,因此相比于term的精确搜索,match是分词匹配搜索
bool查询

bool 查询可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符:
must :: 多个查询条件的完全匹配,相当于 and 。
must_not :: 多个查询条件的相反匹配,相当于 not 。
should :: 至少有一个查询条件匹配, 相当于 or 。
这些参数可以分别继承一个查询条件或者一个查询条件的数组:

POST /king/_search
{
	"query": {
		"bool": {
			"must": {
				"term": {
					"sex": "男"
				}
			},
			"must_not": {
				"term": {
					"age": "29"
				}
			},
			"should": [
			  {
					"term": {
						"sex": "男"
					}
				},
				{
					"term": {
						"id": 1003
					}
				}
			]
		}
	}
}
过滤查询

查询年龄为20岁的用户。

POST /king/_search
{
	"query": {
		"bool": {
			"filter": {
				"term": {
					"age": 20
				}
			}
		}
	}
}

3.官方的练习

批量导入测试数据

该数据是使用www.json- http://generator.com/生成的,因此请忽略数据的实际值和语义,因为它们都是随机生成的。您可以从这里下载示例数据集(accounts.json)。将其提取到当前目录,然后按如下方式将其加载到集群中:

curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_bulk?pretty&refresh" --data-binary "@accounts.json"

3.1给指定id加点年龄(age)

POST /bank/_update/1
{
  "script": "ctx._source.age+=5"
}

3.2执行match_all操作,并按帐户余额降序对结果进行排序,并返回前10个

GET /bank/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "balance": {
        "order": "desc"
      }
    }
  ]
}

3.3如何从搜索中返回两个字段,即帐号和余额

GET /bank/_search
{
  "query": {
    "match_all": {}
  }, 
  "_source": ["account_number","balance"]
}

3.4返回帐户为20的

GET /bank/_search
{
  "query": {
    "match": {
      "account_number": "20"
    }
  }
}

3.5返回地址中包含“mill”的所有帐户

GET /bank/_search
{
  "query": {
    "match": {
      "address": "mill"
    }
  }
}

3.6返回地址中包含“mill”或“lane”的所有帐户

GET /bank/_search
{
  "query": {
    "match": {
      "address": "mill lane"
    }
  }
}

3.7返回地址中包含“mill”和“lane”的所有帐户

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "mill"
          }
        },
        {
          "match": {
            "address": "lane"
          }
        }
      ]
    }
  }
}

3.8地址中既不包含“mill”也不包含“lane”的所有帐户

GET /bank/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
            "address": "mill"
          }
        },
        {
          "match": {
            "address": "lane"
          }
        }
      ]
    }
  }
}

3.9返回所有40岁但不居住在ID的人(state不等于ID)的账户

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "age": {
              "value": 40
            }
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "state": "ID"
          }
        }
      ]
    }
  }
}

3.10使用bool查询返回余额在20000到30000之间的所有帐户,包括余额。换句话说,我们希望找到余额大于或等于20000,小于或等于30000的账户

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "balance": {
              "gte": 20000,
              "lte": 30000
            }
          }
        }
      ]
    }
  }
}

3.11按状态(state)对所有帐户进行分组,然后返回按count降序排列的前10个

GET /bank/_search
{
  "aggs": {
    "status_group": {
      "terms": {
        "field": "state.keyword"
      }
    }
  },
  "size": 0
}

3.12按状态计算平均帐户余额(同样只针对按count降序排列的前10个状态)

GET /bank/_search
{
  "aggs": {
    "status_group": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "balance_avg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0
}

3.13基于之前(12)的聚合,我们现在按降序对平均余额排序

GET /bank/_search
{
  "aggs": {
    "status_group": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "balance_avg": "asc"
        }
      },
      "aggs": {
        "balance_avg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0
}

3.14按照年龄等级(20-29岁,30-39岁,40-49岁)分组,然后按性别分组,最后得到每个年龄等级,每个性别的平均账户余额

GET /bank/_search
{
  "aggs": {
    "age_range": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          },{
            "from": 30,
            "to": 40
          },{
            "from": 40,
            "to": 50
          }
        ]
      },
      "aggs": {
        "gender_group": {
          "terms": {
            "field": "gender.keyword"
          },
          "aggs": {
            "balance_avg": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      }
    }
  },
  "size": 0
}

4.中文分词

4.1Analyzer 的组成

  • Character Filters (针对原始文本处理,例如,可以使用字符过滤器将印度阿拉伯数字( )转换为其等效的阿拉伯语-拉丁语(0123456789))
  • Tokenizer(按照规则切分为单词),将把文本 “Quick brown fox!” 转换成 terms [Quick, brown, fox!],tokenizer 还记录文本单词位置以及偏移量。
  • Token Filter(将切分的的单词进行加工、小写、刪除 stopwords,增加同义词)

4.2elasticsearch内置分词器

Standard默认分词器 按词分类 小写处理
Simple按照非字母切分,非字母则会被去除 小写处理
**Stop **小写处理 停用词过滤(the,a, is)
**Whitespace **按空格切分
**Keyword **不分词,当成一整个 term 输出
**Patter **通过正则表达式进行分词 默认是 \W+(非字母进行分隔)
**Language **提供了 30 多种常见语言的分词器

4.3分词api

POST /_analyze
{
  "analyzer":"standard",
  "text":"tai bai"
}

POST /_analyze
{
  "analyzer":"standard",
  "text":"决战到天亮"
}

英文分词 一般以空格分隔,中文分词的难点在于,在汉语中没有明显的词汇分界点,如果分隔不正确就会造成歧义。

常用中文分词器,IK、jieba、THULAC等,推荐使用IK分词器。

4.4ik分词器安装

IK分词器 Elasticsearch插件地址:https://github.com/medcl/elasticsearch-analysis-ik

注意选择对应es的版本

在这里插入图片描述

1.下载项目 zip包

在这里插入图片描述

2.解压项目

3.进入项目跟目录 使用maven编译打包此项目

mvn clean
mvn compile
mvn package

4.执行完上面命令后 在{project_path}/elasticsearch-analysis-ik/target/releases/elasticsearch-analysis-ik-*.zip会有个zip,上传到linux elasticsearch 插件目录, 如: plugins/ik 注意在plugins下新建ik目录 将zip包上传到ik目录下

在这里插入图片描述

5.使用unzip命令解压zip包,没有unzip的 可先下载unzip 命令:yum install -y unzip zip

6.解压之后删除原来的zip包

7.检查是否需要修改版本信息

vim {path}/plugins/ik/plugin-descriptor.properties

在这里插入图片描述

8.重启 ik插件安装完成

在这里插入图片描述

9.测试中文分词器效果

POST /_analyze
{
  "analyzer": "ik_max_word",   //或者  //ik_smart
  "text": "决战到天亮"
}

4.5拼音分词器

1.下载对应版本的zip包https://github.com/medcl/elasticsearch-analysis-pinyin/releases

2.可在Windows解压好,在plugins下创建pinyin文件夹

3.将解压内容放置在pinyin文件夹,重启

4.6自定义分词器

接受参数

tokenizer一个内置的或定制的tokenizer。(必需)
char_filter一个可选的内置或自定义字符过滤器数组。
filter一个可选的内置或定制token过滤器数组。
PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type": "custom", 
          "tokenizer": "standard",
          "char_filter": [
            "html_strip"     //过滤HTML标签
          ],
          "filter": [
            "lowercase",    //转小写
            "asciifolding"  //ASCII-折叠令牌过滤器  例如 à to a
          ]
        }
      }
    }
  }
}


POST my_index/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": "Is this <b>déjà vu</b>?"
}

创建一个中文+拼音的分词器(中文分词后拼音分词)

PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "ik_pinyin_analyzer": {
          "type": "custom",
          "tokenizer": "ik_smart",
          "filter": [
            "pinyin_max_word_filter"
          ]
        },
        "ik_pingying_smark": {
          "type": "custom",
          "tokenizer": "ik_smart",
          "filter": [
            "pinyin_smark_word_filter"
          ]
        }
      },
      "filter": {
        "pinyin_max_word_filter": {
          "type": "pinyin",
          "keep_full_pinyin": "true",  #分词全拼如雪花 分词xue,hua
          "keep_separate_first_letter": "true",#分词简写如雪花 分词xh
          "keep_joined_full_pinyin": true  #分词会quanpin 连接 比如雪花分词 xuehua
        },
        "pinyin_smark_word_filter": {
          "type": "pinyin",
          "keep_separate_first_letter": "false", #不分词简写如雪花 分词不分词xh
          "keep_first_letter": "false"     #不分词单个首字母 如雪花 不分词 x,h
        }
      }
    }
  }
}


PUT /my_index/_mapping
  {
  "properties": {
      "productName": {
          "type": "text",
          "analyzer": "ik_pinyin_analyzer",  #做文档所用的分词器
          "search_analyzer":"ik_pingying_smark"   #搜索使用的分词器
      }
  }
}

POST /my_index/_doc
{
  "productName": "雪花啤酒100L"
}


GET /my_index/_search
{
  "query": {
    "match": {
      "productName": "雪Hua"
    }
  }
}

5.全文搜索

5.1构建数据

PUT /test
{
	"settings": {
		"index": {
			"number_of_shards": "1",
			"number_of_replicas": "0"
		}
	},
	"mappings": {
		"properties": {
			"age": {
				"type": "integer"
			},
			"email": {
				"type": "keyword"
			},
			"name": {
				"type": "text"
			},
			"hobby": {
				"type": "text",
				"analyzer": "ik_max_word"
			}
		}
	}
}

POST _bulk
{ "create" : { "_index" : "test","_id": "1000"} }
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球、乒乓球、足球"}
{ "create" : { "_index" : "test","_id": "1001"} }
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球、乒乓球、足球、篮球"}
{ "create" : { "_index" : "test","_id": "1002"} }
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球、篮球、游泳、听音乐"}
{ "create" : { "_index" : "test","_id": "1003"} }
{"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步、游泳、篮球"}
{ "create" : { "_index" : "test","_id": "1004"} }
{"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐、看电影、羽毛球"}

5.2单词搜索

POST /test/_search
{
	"query": {
		"match": {
			"hobby": "音乐"
		}
	},
	"highlight": {
		"fields": {
			"hobby": {}
		}
	}
}

5.3多词搜索

//搜索包含音乐和篮球的
POST /test/_search
{
	"query": {
		"match": {
			"hobby": "音乐 篮球"
		}
	},
	"highlight": {
		"fields": {
			"hobby": {}
		}
	}
}

//搜索包含音乐还有篮球的(and)
POST /test/_search
{
	"query": {
		"match": {
			"hobby": {
				"query": "音乐 篮球",
				"operator": "and"
			}
		}
	},
	"highlight": {
		"fields": {
			"hobby": {}
		}
	}
}

GET /goods/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "price": {
              "gte": 1000,
              "lte": 2000
            }
          }
        },
        {
          "match": {
            "name": "2018女鞋"
          }
        },
        {
          "match": {
            "spec": "红色 黑色"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "spec": "蓝色"
          }
        }
      ]
    }
  }
}

//在Elasticsearch中也支持这样的查询,通过minimum_should_match来指定匹配度,如:70%;
POST /test/_search
{
	"query": {
		"match": {
			"hobby": {
				"query": "游泳 羽毛球",
				"minimum_should_match": "70%"
			}
		}
	},
	"highlight": {
		"fields": {
			"hobby": {}
		}
	}
}

5.4组合搜索

//搜索结果中必须包含篮球,不能包含音乐,如果包含了游泳,那么它的相似度更高。
POST /test/_search
{
	"query": {
		"bool": {
			"must": {
				"match": {
					"hobby": "篮球"
				}
			},
			"must_not": {
				"match": {
					"hobby": "音乐"
				}
			},
			"should": [{
				"match": {
					"hobby": "游泳"
				}
			}]
		}
	},
	"highlight": {
		"fields": {
			"hobby": {}
		}
	}
}


//默认情况下,should中的内容不是必须匹配的,如果查询语句中没有must,那么就会至少匹配其中一个。当然了,
//也可以通过minimum_should_match参数进行控制,该值可以是数字也可以的百分比。
//minimum_should_match为2,意思是should中的三个词,至少要满足2个

POST /test/_search
{
	"query": {
		"bool": {
			"should": [{
					"match": {
						"hobby": "游泳"
					}
				},
				{
					"match": {
						"hobby": "篮球"
					}
				},
				{
					"match": {
						"hobby": "音乐"
					}
				}
			],
			"minimum_should_match": 2
		}
	},
	"highlight": {
		"fields": {
			"hobby": {}
		}
	}
}

5.5权重

搜索关键字为“游泳篮球”,如果结果中包含了“音乐”权重为10,包含了“跑步”权重为2。

POST /test/_search
{
	"query": {
		"bool": {
			"must": {
				"match": {
					"hobby": {
						"query": "游泳篮球",
						"operator": "and"
					}
				}
			},
			"should": [{
					"match": {
						"hobby": {
							"query": "音乐",
							"boost": 10
						}
					}
				},
				{
					"match": {
						"hobby": {
							"query": "跑步",
							"boost": 2
						}
					}
				}
			]
		}
	},
	"highlight": {
		"fields": {
			"hobby": {}
		}
	}
}

6.写在最后

本篇博客主要简单介绍elasticsearch的分词和简单的API。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐