共计 4665 个字符,预计需要花费 12 分钟才能阅读完成。
全文搜索是目前非常常见的需求,而开源的elasticsearch(以下简称es)更是全文搜索引擎的首选。github,维基百科,Stack Overflow,百度等都在使用。
es底层的开源库是Lucence,但是我们直接使用Lucence,还要再学习很多Lucene相关的知识,所以我们需要用一个高级语言来调用它。而elasticsearch就是使用java对Lucence的封装,提供restful API接口,开箱即用。当然不仅仅只是封装接口而已,还是一个分布式的近实时的文档存储,一个分布式的近实时的分析搜索引擎,能扩展上百个服务节点,支持PB级别的结构化和非结构化的数据。
既然这么牛逼,那我们要做的就是,用它!用它!用它!
一、elasticsearch基本概念
Node、Cluster
es是一个分布式的数据库,可以多台服务器协同工作,每台服务器可以运行多个es实例,每个实例是一个Node,一组Node构成一个集群Cluster
Index
es会索引所有字段,经过处理后生成一个反向索引(Inverted Index)。查找数据就是直接查找该索引。
Index相当于Mysql的数据库,每个Index的名字必须是小写的。
// 查看当前节点的所有Index
curl -X GET 'http://localhost:9200/_cat/indices?v'
Document
Index里面单条记录称为 Document (文档)。同一个Index里的document,结构(scheme)相同,有利于提高搜索效率。
Type
Document可以分组,分组叫做Type。不同的Type里相同的字段应该有相同的结构。比如id在type1里是int,那么在type2里就不能是string
es 6.x版开始只允许每个Index包含一个Type,7.x之后的版本就彻底移除Type了,也就是现在我们要学习的版本就没有Type这个概念了,Index里面直接就是Document。
二、新建和删除Index
// 新建一个名为 task 的 Index
curl -X PUT 'http://localhost:9200/task'
// 服务器返回一个JSON对象
{
"acknowledged":true, //操作成功
"shards_acknowledged":true
}
// 删除Index
curl -X DELETE 'http://localhost:9200/task'
三、中文分词设置
安装中文分词插件,这里选择 IK,国内比较常用的,当然还有其他的。
安装好了,重启es,就会自动加载插件。
// 凡是需要搜索的中文字段,都需要单独设置一下
$ curl -X PUT 'localhost:9200/task' -d '
{
"mappings": {
"_doc": {
"properties": {
"user": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
}
上面的代码里,会新建一个名为task的Index,里面有一个_doc的type,es6.x以后不支持新增其他的type了。_doc里有三个字段 user,title,desc。
这三个字段都是中文,类型都是text,所以需要指定中文分词器,es的分词器称为 analyzer。search_analyzer是搜索词的分词器。ik_max_word是分词器插件ik提供,可以对文本进行最大数量的分词。
四、Document操作
4.1 新增记录
两种方式新增,
- PUT指定id
- POST不指定id
// PUT指定id新增记录
$ curl -X PUT 'localhost:9200/task/_doc/1' -d '
{
"createdBy": "张三",
"product": "BIZ",
"category": "ROBOT"
}
// 返回json
{
"_index" : "task", //索引
"_type" : "_doc", //type
"_id" : "1", //记录的id
"_version" : 1, //记录更新的版本数,
"result" : "created", // 再次指定id PUT,会变成updated
"_shards" : { //_shards表示索引操作的复制过程的信息
"total" : 2, //指示应在其上执行索引操作的分片副本(主分片和副本分片)的数量
"successful" : 1, //表示索引操作成功的分片副本数
"failed" : 0
},
"_seq_no" : 2, //序号,从0编码,第三次就是2
"_primary_term" : 1
}
一个document只会存放在一个shard里面,而一个shard有个replica,而我这只有个shard,没有replica,所有只有一个successful。
_primary_term:_primary_term也和_seq_no一样是一个整数,每当Primary Shard发生重新分配时,比如重启,Primary选举等,_primary_term会递增1。
_primary_term主要是用来恢复数据时处理当多个文档的_seq_no一样时的冲突,比如当一个shard宕机了,raplica需要用到最新的数据,就会根据_primary_term和_seq_no这两个值来拿到最新的document。
注:Index不能写错,写错不会es不会报错,会直接生成指定的index
// POST不指定id 新增记录
$ curl -X POST 'localhost:9200/task/_doc' -d '
{
"createdBy": "李四",
"product": "BIZ",
"category": "ROBOT"
}
// 返回的json
{
"_index" : "task",
"_type" : "_doc",
"_id" : "uJYweXIB6lu8kgtqGLDa", //没有指定id,id就是一个自动生成的随机字符串
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 5,
"_primary_term" : 1
}
4.2 查看记录
// 根据id查看
$ curl -X GET 'localhost:9200/task/_doc/1'
// 返回的json
{
"_index" : "task",
"_type" : "_doc",
"_id" : "1",
"_version" : 5,
"_seq_no" : 4,
"_primary_term" : 1,
"found" : true,
"_source" : {
"createdBy" : "张三",
"product" : "BIZ",
"category" : "ROBOT"
}
}
4.3 更新记录
更新记录的操作跟上面使用 PUT 指定 id 新增记录的操作是一样的,PUT指定id,有就更新,没有就新增
4.4 删除记录
$ curl -X DELETE 'localhost:9200/task/_doc/1'
// 返回的json
{
"_index" : "task",
"_type" : "_doc",
"_id" : "1",
"_version" : 6,
"result" : "deleted",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 7,
"_primary_term" : 1
}
五、数据查询
5.1 查询所有记录
// 使用GET请求
$ curl 'localhost:9200/task/_doc/_search'
上面这个请求会报错,! Deprecation: [types removal] Specifying types in search requests is deprecated.因为type被移除了,所有不需要指定type了,改成下面这个写法
// 使用GET请求
$ curl 'localhost:9200/task/_search'
{
"took" : 0, //耗时,单位毫秒
"timed_out" : false, //是否差超时
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2, // 共几条记录,默认10000条,需要返回更多要修改配置
"relation" : "eq"
},
"max_score" : 1.0, //最高的匹配程度
"hits" : [ //命中的所有记录
{
"_index" : "task",
"_type" : "_doc",
"_id" : "uJYweXIB6lu8kgtqGLDa",
"_score" : 1.0, // 匹配的程序,默认是按照这个字段降序排列
"_source" : {
"createdBy" : "李四",
"product" : "BIZ",
"category" : "ROBOT"
}
},
{
"_index" : "task",
"_type" : "_doc",
"_id" : "uZYxeXIB6lu8kgtqErDo",
"_score" : 1.0,
"_source" : {
"createdBy" : "李四",
"product" : "BIZ",
"category" : "ROBOT"
}
}
]
}
}
5.2 全文搜索
es的查询需要使用特定的查询语法,叫Elasticsearch-DSL(Domain-specific language),特定领域语言
单关键词,使用 Match 查询
//使用 match 查询
//查询createdBy中包含”三“的记录
GET /task/_search
{
"query": {
"match": {
"createdBy": "三"
}
},
"from": 1, //偏移
"size": 100 //不指定默认返回10条结果
}
//查询字段里可以放多个词,空格隔开,默认的operator是or
GET /task/_search
{
"query": {
"match": {
"createdBy": "四 三"
}
}
}
// 查询createdBy同时包含"三 四"两个词
{
"query": {
"match": {
"createdBy": {
"query": "张三 李四",
"operator": "and"
}
}
}
}
多个关键词,必须使用 bool查询
GET /task/_search
{
"query": {
"bool": {
"must": [
{"match": {"createdBy": "张三"}},
{"match": {"category": "ROBOT"}}
]
}
}
}
- must:必须同时满足,
- must_not:表示不满足
- minimum_should_match:表示最小匹配度
GET /task/_search
{
"bool": {
"should": [
{ "term": { "body": "how"}},
{ "term": { "body": "not"}},
{ "term": { "body": "to"}},
{ "term": { "body": "be"}}
],
"minimum_should_match": 3
}
}