Elasticsearch-使用篇

一、Elasticsearch使用-linux

Es常用操作

索引操作

Elasticsearch是使用RESTful风格的http请求访问操作的,请求参数和返回值都是Json格式的,我们可以使用kibana发送http请求操作ES。

域的属性

属性 作用
index 该域是否创建索引。只有值设置为true,才能根据该域的关键词查询文档。
type 域的类型
store 是否单独存储。如果设置为true,则该域能够单独查询。
analyzer standard、(ik_smart、ik_max_word)、pinyin、自定义分词器
  • type
核心类型 具体类型
字符串类型 text
整数类型 long, integer, short, byte
浮点类型 double, float
日期类型 date
布尔类型 boolean
数组类型 array
对象类型 object
不分词的字符串 keyword
根据关键词查询文档
  • index
# 根据关键词查询文档
# 创建索引:"index": true
PUT /student1
{
    
    
  "mappings": {
    
    
    "properties": {
    
    
      "name":{
    
    
        "type": "text",
        "index": true
      }
    }
  }
}
# 不创建索引:"index": false
PUT /student2
{
    
    
  "mappings": {
    
    
    "properties": {
    
    
      "name":{
    
    
        "type": "text",
        "index": false
      }
    }
  }
}
# 创建文档
POST /student1/_doc/1
{
    
    
  "name":"i love java"
}
POST /student2/_doc/1
{
    
    
  "name":"i love java"
}
# 搜索文档
GET /student1/_search
{
    
    
  "query": {
    
    
    "term": {
    
    
        "name": "love"
    }
  }
}
GET /student2/_search
{
    
    
  "query": {
    
    
    "term": {
    
    
        "name": "love"
    }
  }
}
单独查询某个域
// 单独查询某个域:
GET /索引名/_search
{
    
    
  "stored_fields": ["域名"]
}

分词器

ES文档的数据拆分成一个个有完整含义的关键词,并将关键词与文档对应,这样就可以通过关键词查询文档。要想正确的分词,需要选择合适的分词器。

⑴默认分词器:standard
  • standard analyzer:Elasticsearch默认分词器,根据空格和标点符号对英文进行分词,会进行单词的大小写转换。
  • 默认分词器是英文分词器,对中文的分词是一字一词。
GET /_analyze
{
    
    
  "text": ["i love spring"],
  "analyzer": "standard"
}
⑵IK分词器
  • IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。提供了两种分词算法:
    • ik_smart:最少切分
    • ik_max_word:最细粒度划分
GET /_analyze
{
    
    
  "text": ["我爱百战程序员"],
  "analyzer": "ik_smart"
}
----------------------------------------
GET /_analyze
{
    
    
  "text": ["我爱百战程序员"],
  "analyzer": "ik_max_word"
}
IK分词器词典
  • IK分词器根据词典进行分词,词典文件在IK分词器的config目录中。
    • main.dic:IK中内置的词典。记录了IK统计的所有中文单词。
    • IKAnalyzer.cfg.xml:用于配置自定义词库。
  • 主配置文件
:配置文件名 :配置文件名
cd /usr/local/elasticsearch1/plugins/analysis-ik/config/

vim 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_dict.dic</entry>
    <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords">ext_stopwords.dic</entry>
    <!--用户可以在这里配置远程扩展字典 -->
    <!-- <entry key="remote_ext_dict">words_location</entry> -->
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
  • 自定义词
cd /usr/local/elasticsearch1/plugins/analysis-ik/config/

# 新增词
vim ext_dict.dic
# 禁止词
vim ext_stopwords.dic
⑶拼音分词器
GET /_analyze
{
    
    
  "text": ["我爱英雄联盟"],
  "analyzer": "pinyin"
}
⑷自定义分词器
  • 真实开发中我们往往需要对一段内容既进行文字分词,又进行拼音分词,此时我们需要自定义ik+pinyin分词器。
  • 在创建索引时自定义分词器
PUT /索引名
{
    
    
  "settings": {
    
    
    "analysis": {
    
    
      "analyzer": {
    
    
        "ik_pinyin": {
    
    //自定义分词器名
          "tokenizer": "ik_max_word",// 基本分词器
          "filter": "pinyin_filter"// 配置分词器过滤
        }
      },
      "filter": {
    
    // 分词器过滤时配置另一个分词器,相当于同时使用两个分词器
        "pinyin_filter": {
    
    // 另一个分词器
          "type": "pinyin",// 拼音分词器的配置
          "keep_separate_first_letter": false,// 是否分词每个字的首字母
          "keep_full_pinyin": true,// 是否分词全拼
          "keep_original": true,// 是否保留原始输入
          "remove_duplicated_term": true// 是否删除重复项
        }
      }
    }
  },
  "mappings": {
    
    
    "properties": {
    
    
      "域名1": {
    
    
        "type": 域的类型,
        "store": 是否单独存储,
        "index": 是否创建索引,
        "analyzer": 分词器
      },
      "域名2": {
    
    
        ...
      }
    }
  }
}
实例
PUT /student3
{
    
    
  "settings": {
    
    
    "analysis": {
    
    
      "analyzer": {
    
    
        "ik_pinyin":{
    
    
          "tokenizer": "ik_max_word",
          "filter": "pinyin_filter"
        }
      },
      "filter": {
    
    
        "pinyin_filter":{
    
    
          "type": "pinyin",
          "keep_separate_first_letter": false,
          "keep_full_pinyin": true,
          "keep_original": true,
          "remove_duplicated_term": true
        }
      }
    }
  },
  "mappings": {
    
    
    "properties": {
    
    
      "name": {
    
    
        "type": "text",
        "store": true,
        "index": true, 
        "analyzer": "ik_pinyin"
      },
      "age": {
    
    
        "type": "integer"
      }
    }
  }
}

①、创建索引(PUT)

创建没有结构的索引

索引添加结构

    POST /索引名/_mapping 
    {
    
    
        "properties":{
    
    
            "域名1":{
    
    
                "type":域的类型,
                "store":是否存储,
                "index":是否创建索引,
                "analyzer":分词器
            },
            "域名2":{
    
    
 				...
            }
        }
    }
实例
# 创建索引
put /student
# 创建结构
POST /student/_mapping
{
    
    
  "properties":{
    
    
    "id":{
    
    
      "type":"integer"
    },
    "name":{
    
    
      "type":"text"
    },
    "age":{
    
    
      "type":"integer"
    }
  }
}
创建有结构的索引
    PUT /索引名 
    {
    
    
        "mappings":{
    
    
            "properties":{
    
    
                "域名1":{
    
    
                    "type":域的类型,
                    "store":是否单独存储,
                    "index":是否创建索引,
                    "analyzer":分词器
                },
                "域名2":{
    
    
				 ...
                }
            }
        }
    }
实例
# 创建索引&结构
PUT /student1
{
    
    
  "mappings": {
    
    
    "properties": {
    
    
      "id":{
    
    
        "type": "integer"
      },
      "name":{
    
    
        "type": "text"
      },
      "age":{
    
    
        "type": "integer"
      }
    }
  }
}

②、删除索引(DELETE)

DELETE /索引名
实例
DELETE /student1

文档操作

①、新增/修改文档

id值不写时自动生成文档id,id和已有id重复时修改文档

POST /索引/_doc/[id值]
{
    
    
 "field名":field值
}
实例
# 新增/修改文档
POST /student/_doc/1
{
    
    
  "id":1,
  "name":"bz",
  "age":20
}

②、查询文档

实例 作用
GET /索引/_doc/id值 根据id查询文档
GET /索引/_mget 根据id批量查询文档
GET /索引/_search 查询所有文档
POST /索引/_doc/id值/_update 修改文档部分字段
根据id查询文档
# 根据id查询文档
GET /student/_doc/1
根据id批量查询文档
# 根据id批量查询文档
GET /student/_mget
{
    
    
  "docs":[
    {
    
    "_id":1},
    {
    
    "_id":2}
  ]
}
查询所有文档
# 查询所有文档
GET /student/_search
{
    
    
  "query": {
    
    
    "match_all": {
    
    }
  }
}
修改文档部分字段
  • Elasticsearch执行删除操作时,ES先标记文档为deleted状态,而不是直接物理删除。当ES存储空间不足或工作空闲时,才会执行物理删除操作。
  • Elasticsearch执行修改操作时,ES不会真的修改Document中的数据,而是标记ES中原有的文档为deleted状态,再创建一个新的文档来存储数据。
# 修改文档部分内容
POST /student/_doc/2/_update
{
    
    
  "doc":{
    
    
    "name":"tttttttttttttt"
  }
}

③、删除文档

DELETE /索引/_doc/id值
实例
# 删除文档 
DELETE /student/_doc/1

Elasticsearch搜索文档

GET /索引/_search
{
    
    
 "query":{
    
    
        搜索方式:搜索参数
   }
}
搜索方式 搜索参数 含义
match_all {} 查询所有文档
match {搜索字段:搜索条件} 将查询条件分词后再进行搜索。
range {搜索字段:{“gte”:最小值,“lte”:最大值 }} 对数字类型的字段进行范围搜索
match_phrase {搜索字段:搜索条件} 搜索条件不做任何分词解析
term/terms

创建搜索数据

# 搜索文档
PUT /students
{
    
    
  "mappings": {
    
    
    "properties": {
    
    
      "id": {
    
    
        "type": "integer",
        "index": true
      },
      "name": {
    
    
        "type": "text",
        "store": true,
        "index": true,
        "analyzer": "ik_smart"
      },
      "info": {
    
    
        "type": "text",
        "store": true,
        "index": true,
        "analyzer": "ik_smart"
      }
    }
  }
}

# 添加数据
POST /students/_doc/
{
    
    
  "id":1,
  "name":"IT小熊",
  "info":"I love java"
}
POST /students/_doc/
{
    
    
  "id":2,
  "name":"美羊羊",
  "info":"美羊羊是羊村最漂亮的羊"
}
POST /students/_doc/
{
    
    
  "id":3,
  "name":"懒洋洋",
  "info":"懒洋洋的成绩不是很好"
}
POST /students/_doc/
{
    
    
  "id":4,
  "name":"小灰灰",
  "info":"小灰灰的成绩比较小"
}
POST /students/_doc/
{
    
    
  "id":5,
  "name":"沸羊羊",
  "info":"沸羊羊喜欢美羊羊"
}
POST /students/_doc/
{
    
    
  "id":6,
  "name":"灰太狼",
  "info":"灰太狼是小灰灰的父亲,口头禅是我一定会回来的"
}

①、简单搜索

⑴查询所有文档

格式:

{
    
    
 "query":{
    
    
        "match_all":{
    
    }
   }
}
实例
# 查询所有文档
GET /students/_search
{
    
    
  "query": {
    
    
    "match_all": {
    
    }
  }
}
⑵全文检索(match)

将查询条件分词后再进行搜索。

在搜索时关键词有可能会输入错误,ES搜索提供了自动纠错功能,即ES的模糊查询。使用match方式可以实现模糊查询。模糊查询对中文的支持效果一般,我们使用英文数据测试模糊查询。

格式:

{
    
    
 "query":{
    
    
        "match":{
    
    
            搜索字段:搜索条件
       }
   }
}
实例一
# 全文检索(包含其中的词,就会被检索到)
GET /students/_search
{
    
    
  "query": {
    
    
    "match": {
    
    
      "info": "我喜欢成绩好的"
    }
  }
}
实例二


----------------------------------
纠错
GET /students/_search
{
    
    
  "query": {
    
    
    "match": {
    
    
        "info": {
    
    
            "query":"love",
            "fuzziness":1
        }
    }
  }
}
⑶范围搜索(range)

对数字类型的字段进行范围搜索

格式:

{
    
    
 "query":{
    
    
        "range":{
    
    
            搜索字段:{
    
    
                "gte":最小值,
                "lte":最大值
           }
       }
   }
}
gt/lt:大于/小于
gte/lte:大于等于/小于等于
实例
# 范围搜索
GET /students/_search
{
    
    
  "query": {
    
    
    "range": {
    
    
      "id": {
    
    
        "gte": 1,
        "lte": 3
      }
    }
  }
}
⑷短语检索

搜索条件不做任何分词解析,在搜索字段对应的倒排索引中精确匹配。

格式:

{
    
    
 "query":{
    
    
        "match_phrase":{
    
    
            搜索字段:搜索条件
       }
   }
}
实例
# 短语检索
GET /students/_search
{
    
    
  "query": {
    
    
    "match_phrase": {
    
    
      "info": "喜欢"
    }
  }
}
⑸单词/词组搜索

搜索条件不做任何分词解析,在搜索字段对应的倒排索引中精确匹配

格式:

{
    
    
 "query":{
    
    
        "term/terms":{
    
      
 		搜索字段: 搜索条件
       }
   }
}
实例
# 单词检索
GET /students/_search
{
    
    
  "query": {
    
    
    "terms": {
    
    
      "info": [
        "成绩",
        "喜欢"
      ]
    }
  }
}

②、复合搜索

方式 含义
must 必须满足的条件
should 多个条件有任意一个满足即可
must_not 必须不满足的条件

格式:

GET /索引/_search
{
    
    
  "query": {
    
    
    "bool": {
    
    
      // 必须满足的条件
      "must": [
        搜索方式:搜索参数,
        搜索方式:搜索参数
      ],
      // 多个条件有任意一个满足即可
      "should": [
        搜索方式:搜索参数,
        搜索方式:搜索参数
      ],
      // 必须不满足的条件
      "must_not": [
        搜索方式:搜索参数,
        搜索方式:搜索参数
      ]
    }
  }
}
实例
# 复合搜索
GET /students/_search
{
    
    
  "query": {
    
    
    "bool": {
    
    
      "must": [
        {
    
    
          "match": {
    
    
            "info": "美羊羊喜欢成绩好的同学"
          }
        }
      ],
      "must_not": [
        {
    
    
          "range": {
    
    
            "FIELD": {
    
    
              "gte": 1,
              "lte": 3
            }
          }
        }
      ]
    }
  }
}

③、结果排序

ES中默认使用相关度分数实现排序,可以通过搜索语法定制化排序。

由于ES对text类型字段数据会做分词处理,使用哪一个单词做排序都是不合理的,所以 ES中默认不允许对text类型的字段做排序。如果需要使用字符串做结果排序,可以使用 keyword类型的字段作为排序依据,因为keyword字段不做分词处理。

格式:

GET /索引/_search
{
    
    
  "query": 搜索条件,
  "sort": [
    {
    
    
      "字段1":{
    
    
        "order":"asc"
      }
    },
    {
    
    
      "字段2":{
    
    
        "order":"desc"
      }
    }
  ]
}
实例
# 结果排序
GET /students/_search
{
    
    
  "query": {
    
    
    "match": {
    
    
      "info": "我喜欢成绩好的同学"
    }
  },
  "sort": [
    {
    
    
      "id": {
    
    
        "order": "desc"
      }
    }
  ]
}

④、分页查询

格式:

GET /索引/_search
{
    
    
 "query": 搜索条件,
 "from": 起始下标,
 "size": 查询记录数
}
实例
# 分页查询
GET /students/_search
{
    
    
  "query": {
    
    
    "match_all": {
    
    }
  },
  "from": 4,
  "size": 4
}

⑤、高亮查询

在进行关键字搜索时,搜索出的内容中的关键字会显示不同的颜色,称之为高亮。

我们可以在关键字左右加入标签字符串,数据传入前端即可完成高亮显示,ES可以对查询出的内容中关键字部分进行标签和样式的设置。

格式:

GET /索引/_search
{
    
    
  "query":搜索条件,
  "highlight":{
    
    
    "fields": {
    
    
      "高亮显示的字段名": {
    
    
        // 返回高亮数据的最大长度
        "fragment_size":100,
        // 返回结果最多可以包含几段不连续的文字
        "number_of_fragments":5
      }
    },
    "pre_tags":["前缀"],
    "post_tags":["后缀"]
  }
}
实例
# 高亮查询
GET /students/_search
{
    
    
  "query": {
    
    
    "match": {
    
    
      "info": "我喜欢成绩好的同学"
    }
  },
  "highlight": {
    
    
    "fields": {
    
    
      "info": {
    
    
        "fragment_size": 100,
        "number_of_fragments": 5
      }
    },
    "pre_tags": ["<em>"],
    "post_tags": ["</em>"]
  }
}

⑥、SQL查询

开源版本的ES并不支持通过Java操作SQL进行查询,如果需要操作 SQL查询,则需要氪金(购买白金版)

格式:

GET /_sql?format=txt
{
    
    
 "query": SQL语句
}
实例
# SQL查询
GET /_sql?format=txt
{
    
    
  "query": "select * from students"
}

ES自动补全

自动补全对性能要求极高,ES不是通过倒排索引来实现的,所以需要将对应的查询字段类型设置为completion。

GET /索引/_search
{
     
     
  "suggest": {
     
     
    "prefix_suggestion": {
     
     // 自定义推荐名
      "prefix": "elastic", // 被补全的关键字
      "completion": {
     
     
        "field": "productName", // 查询的域
        "skip_duplicates": true, //忽略重复结果
        "size": 10 //最多查询到的结果数
      }
    }
  }
}
# 创建索引
PUT /product2
{
    
    
  "mappings": {
    
    
    "properties": {
    
    
      "id":{
    
    
        "type": "integer",
        "store": true,
        "index": true
      },
      "productName":{
    
    
        "type": "completion"
      },
      "productDesc":{
    
    
        "type": "text",
        "store": true,
        "index": true
      }
    }
  }
}

# 准备数据
POST /product2/_doc
{
    
    
  "id":1,
  "productName":"elasticsearch1",
  "productDesc":"elasticsearch1 is a good engine"
}
POST /product2/_doc
{
    
    
  "id":2,
  "productName":"elasticsearch2",
  "productDesc":"elasticsearch2 is a good engine"
}
POST /product2/_doc
{
    
    
  "id":3,
  "productName":"elasticsearch3",
  "productDesc":"elasticsearch3 is a good engine"
}

# 搜索
GET /product2/_search
{
    
    
  "suggest": {
    
    
    "prefix_SUGGESTION": {
    
    
      "prefix": "elastic",
      "completion": {
    
    
        "field": "productName",
        "skip_duplicates": true,
        "size": 10
      }
    }
  }
}

二、Elasticsearch使用-Maven

导入依赖

        <!-- es 依赖 版本和使用的软件相同 -->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.17.0</version>
        </dependency>
        <!-- es 客户端 -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.17.0</version>
        </dependency>

Elasticsearch使用

索引操作

①、创建索引

创建空索引
    // 创建空索引
    @Test
    public void createIndex() throws IOException {
    
    
        // 1.创建客户端对象,连接ES
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.66.66", 9200, "http")));
        // 2.创建请求对象
        CreateIndexRequest request = new CreateIndexRequest("student");
        // 3.发送请求
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        // 4.操作响应结果
        System.out.println(response.index());
        // 5.关闭客户端
        client.close();
    }
给索引添加结构
    // 给索引添加结构
    @Test
    public void mappingIndex() throws IOException {
    
    
        // 1.创建客户端对象,连接ES
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.66.66", 9200, "http")));
        // 2.创建请求对象
        PutMappingRequest request = new PutMappingRequest("student");
        request.source("{\n" +
                " \"properties\":{\n" +
                " \"id\":{\n" +
                " \"type\":\"integer\"\n" +
                "  },\n" +
                " \"name\":{\n" +
                " \"type\":\"text\"\n" +
                "  },\n" +
                " \"age\":{\n" +
                " \"type\":\"integer\"\n" +
                "  }\n" +
                " }\n" +
                "}", XContentType.JSON);
        // 3.发送请求
        AcknowledgedResponse response = client.indices().putMapping(request, RequestOptions.DEFAULT);
        // 4.操作响应结果
        System.out.println(response.isAcknowledged());
        // 5.关闭客户端
        client.close();
    }

②、删除索引

    // 删除索引
    @Test
    public void deleteIndex() throws IOException {
    
    
        // 1.创建客户端对象,连接ES
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new
                HttpHost("192.168.66.66", 9200, "http")));
        // 2.创建请求对象
        DeleteIndexRequest request = new DeleteIndexRequest("student");
        // 3.发送请求
        AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
        // 4.操作响应结果
        System.out.println(response.isAcknowledged());
        // 5.关闭客户端
        client.close();
    }

文档操作

①、新增&修改文档

    //新增&修改文档
    @Test
    public void addDocument() throws IOException {
    
    
        // 1.创建客户端对象,连接ES
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.66.66", 9200, "http")));
        // 2.创建请求对象
        IndexRequest request = new IndexRequest("student").id("1");
        request.source(XContentFactory.jsonBuilder()
                .startObject()
                .field("id", 1)
                .field("name", "i love baizhan")
                .field("age", 20)
                .endObject());
        // 3.发送请求
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        // 4.操作响应结果
        System.out.println(response.status());
        // 5.关闭客户端
        client.close();
    }

②、查询文档

根据id查询文档

    //根据id查询文档
    @Test
    public void findByIdDocument() throws IOException {
    
    
        // 1.创建客户端对象,连接ES
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.66.66", 9200, "http")));
        // 2.创建请求对象
        GetRequest request = new GetRequest("student", "1");
        // 3.发送请求
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        // 4.操作响应结果
        System.out.println(response.getSourceAsString());
        // 5.关闭客户端
        client.close();
    }

③、删除文档

    @Test
    public void DeleteDocument() throws IOException {
    
    
        // 1.创建客户端对象,连接ES
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.66.66", 9200, "http")));
        // 2.创建请求对象
        DeleteRequest request = new DeleteRequest("student", "1");
        // 3.发送请求
        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
        // 4.操作响应结果
        System.out.println(response.status());
        // 5.关闭客户端
        client.close();
    }

搜索操作

①、搜索所有文档

    //搜索所有文档
    @Test
    public void queryAllDocument() throws IOException {
    
    
        // 创建客户端对象,链接ES
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.66.66", 9200, "http")));
        // 创建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        // 创建请求对象
        SearchRequest request = new SearchRequest("student").source(searchSourceBuilder);
        // 发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 输出返回结果
        for (SearchHit hit : response.getHits()) {
    
    
            System.out.println(hit.getSourceAsString());
        }
        // 关闭客户端
        client.close();
    }

②、根据关键词搜索文档

    // 根据关键词搜索文档
    @Test
    public void queryTermDocument() throws
            IOException {
    
    
        // 创建客户端对象,链接ES
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.66.66", 9200, "http")));
        // 创建请求条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery("info", "hunaggang"));
        // 创建请求对象
        SearchRequest request = new SearchRequest("student").source(searchSourceBuilder);
        // 发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 输出返回结果
        for (SearchHit hit : response.getHits()) {
    
    
            System.out.println(hit.getSourceAsString());
        }
        // 关闭客户端
        client.close();
    }

三、Elasticsearch使用-Springboot

导入依赖

        <!-- es 起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

报错:版本控制

    <properties>
        <java.version>11</java.version>
        <!-- 版本控制:和es版本一致 -->
        <elasticsearch.version>7.17.0</elasticsearch.version>
    </properties>

配置文件

# elasticsearch配置
spring:
  elasticsearch:
    uris: http://192.168.66.66:9200

使用Elasticsearch

创建索引

注解 位置
@Document 标记在类上,标记实体类为文档对象
@Id 标记在成员变量上,标记一个字段为主键,该字段的值会同步到ES该文档的id值。
@Field 标记在成员变量上,标记为文档中的域。

一个实体类的所有对象都会存入ES的一个索引中,所以我们在创建实体类时关联ES索引

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "product", createIndex = true)
public class Product {
    
    
    @Id
    @Field(type = FieldType.Integer, store = true, index = true)
    private Integer id;
    @Field(type = FieldType.Text, store = true,
            index = true, analyzer = "ik_max_word",
            searchAnalyzer = "ik_max_word")
    private String productName;
    @Field(type = FieldType.Text, store = true,
            index = true, analyzer = "ik_max_word",
            searchAnalyzer = "ik_max_word")
    private String productDesc;
}

@Document

标记在类上,标记实体类为文档对象,一般有如下属性:

属性 意义
indexName 对应索引的名称
createIndex 是否自动创建索引

@Id

标记在成员变量上,标记一个字段为主键,该字段的值会同步到ES该文档的id值。

@Field

标记在成员变量上,标记为文档中的域,一般有如下属性:

属性 意义
type 域的类型
index 是否创建索引,默认是 true
store 是否单独存储,默认是 false
analyzer 分词器
searchAnalyzer 搜索时的分词器

操作文档-继承接口

创建Repository接口继承ElasticsearchRepository,该接口提供了文档的增删改查方法

public interface ProductRepository extends ElasticsearchRepository<Product,Integer> {
    
    
}

①、自带方法

新增/修改文档
    @Test
    public void addDocument(){
    
    
        Product product = new Product(1, "iphone30", "iphone30是苹果最新手机");
        Product save = productRepository.save(product);
        System.out.println(save);
    }
查询所有
    @Test
    public void findAllDocument() {
    
    
        Iterable<Product> all = repository.findAll();
        all.forEach(product -> {
    
    
            System.out.println(product);
        });
    }
通过id查询
    @Test
    public void findDocumentById(){
    
    
        Optional<Product> product = repository.findById(1);
        System.out.println(product.get());
    }

②、自定义

  • 创建数据-使用DSL语句查询文档
    @Test
    public void addDocument() {
    
    
        // 添加一些数据
        repository.save(new Product(2, "三体1", "三体1是优秀的科幻小说"));
        repository.save(new Product(3, "三体2", "三体2是优秀的科幻小说"));
        repository.save(new Product(4, "三体3", "三体3是优秀的科幻小说"));
        repository.save(new Product(5, "elasticsearch", "elasticsearch是基于lucene开发的优秀的搜索引擎"));
    }
⑴@Query

query后的json对象称为DSL语句,我们可以在接口方法上使用@Query注解自定义DSL语句查询。

占位符:?0

public interface ProductRepository extends ElasticsearchRepository<Product,Integer> {
    
    
    @Query("{\n" +
            "    \"match\": {\n" +
            "      \"productDesc\": \"?0\"\n" +
            "    }\n" +
            "  }")
    List<Product> findByProductDescMatch(String keyword);
}
⑵命名方法命名规则
  • 只需在Repository接口中按照SpringDataES的规则命名方法,该方法就能完成相应的查询。
  • 规则:查询方法以findBy开头,涉及查询条件时,条件的属性用条件关键字连接。
关键字 命名规则 解释 示例
and findByField1AndField2 根据Field1和Field2 获得数据 findByTitleAndContent
or findByField1OrField2 根据Field1或Field2 获得数据 findByTitleOrContent
is findByField 根据Field获得数据 findByTitle
not findByFieldNot 根据Field获得补集数据 findByTitleNot
between findByFieldBetween 获得指定范围的数据 findByPriceBetween
    List<Product> findByProductName(String productName);
    List<Product> findByProductNameOrProductDesc(String productName,String productDesc);
    List<Product> findByIdBetween(Integer startId,Integer endId);
⑶分页查询

使用继承或自定义的方法时,在方法中添加Pageable类型的参数,返回值为Page类型即可进行分页查询。

  • 继承方法实现分页
    @Test
    public void test(){
    
    
        Pageable pageable = PageRequest.of(1,3);
        Page<Product> page = repository.findAll(pageable);
        System.out.println("总条数"+page.getTotalElements());
        System.out.println("总页数"+page.getTotalPages());
        System.out.println("数据"+page.getContent());
    }
------------------------------------------------
        @Test
    public void testFindPage3() {
    
    
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        Pageable pageable = PageRequest.of(0, 2, sort);
        Page<Product> page = repository.findByProductDesc("我喜欢三体", pageable);
        System.out.println("总条数" + page.getTotalElements());
        System.out.println("总页 数" + page.getTotalPages());
        System.out.println("数据" + page.getContent());
    }
  • 自定义方法实现分页
    @Query("{\n" +
            "    \"match\": {\n" +
            "      \"productDesc\": \"?0\"\n" +
            "    }\n" +
            "  }")
    Page<Product> findByProductDesc(String keyword, Pageable pageable);
----------------------------------------------------------
        @Test
    public void testFindPage2() {
    
    
        Pageable pageable = PageRequest.of(1, 2);
        Page<Product> page = repository.findByProductDesc("我喜欢三体", pageable);
        System.out.println("总条数" + page.getTotalElements());
        System.out.println("总页数" + page.getTotalPages());
        System.out.println("数据" + page.getContent());
    }
⑷结果排序

使用继承或自定义的方法时,在方法中添加Sort类型的参数即可进行结果排序。

    @Test
    public void testFindSort() {
    
    
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        Iterable<Product> all = repository.findAll(sort);
        for (Product product : all) {
    
    
            System.out.println(product);
        }
    }

操作文档-template工具类

SpringDataElasticsearch提供了一个工具类ElasticsearchRestTemplate,我们使用该类对象也能对ES进行操作。

①、索引操作

新增

(推荐注解自动创建索引)

    @Test
    public void addIndex() {
    
    
        // 获得索引操作对象
        IndexOperations indexOperations = template.indexOps(Product.class);
        // 创建索引,注:该方法无法设置索引结构,不推荐使用
        indexOperations.create();
    }
删除
    @Test
    public void delIndex() {
    
    
        // 获得索引操作对象
        IndexOperations indexOperations = template.indexOps(Product.class);
        // 删除索引
        indexOperations.delete();
    }

②、文档操作

新增/修改文档:save()

  @Test
    public void addDocument() {
    
    
        Product product = new Product(7, "es1", "es是一款优秀的搜索引擎");
        template.save(product);
    }

删除文档:delete()

    @Test
    public void delDocument() {
    
    
        template.delete("7", Product.class);
    }

③、查询操作

查询文档:SearchHits search(Query query, Classclazz):查询文档,query是查询条件对象,clazz是结果类型。

普通查询
    @Test
    public void searchDocument() {
    
    
        // 1.确定查询方式
        // MatchAllQueryBuilder builder = QueryBuilders.matchAllQuery();
        // TermQueryBuilder builder = QueryBuilders.termQuery("productDesc", "手机");
        MatchQueryBuilder builder = QueryBuilders.matchQuery("productDesc", "我喜欢看科幻小说");
        // 2.构建查询条件
        NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(builder).build();
        // 3.查询
        SearchHits<Product> result = template.search(query, Product.class);
        // 4.处理查询结果
        result.forEach(productSearchHit -> {
    
    
            System.out.println(productSearchHit.getContent());
        });
    }
复杂查询
    //复杂条件查询
    @Test
    public void searchDocument2() {
    
    
        //  String productName ="elasticsearch";
        //  String productDesc = "优秀";
        String productName = null;
        String productDesc = null;
        // 1.确定查询方式
        BoolQueryBuilder builder = QueryBuilders.boolQuery();
        // 如果没有传入参数,查询所有
        if (productName == null && productDesc == null) {
    
    
            MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
            builder.must(matchAllQueryBuilder);
        } else {
    
    
            if (productName != null && productName.length() > 0) {
    
    
                MatchQueryBuilder queryBuilder1 = QueryBuilders.matchQuery("productName", productName);
                builder.must(queryBuilder1);
            }
            if (productDesc != null && productDesc.length() > 0) {
    
    
                MatchQueryBuilder queryBuilder2 = QueryBuilders.matchQuery("productDesc", productDesc);
                builder.must(queryBuilder2);
            }
        }
        // 2.构建查询条件
        NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(builder).build();
        // 3.查询
        SearchHits<Product> result = template.search(query, Product.class);
        // 4.处理查询结果
        for (SearchHit<Product> productSearchHit : result) {
    
    
            Product product = productSearchHit.getContent();
            System.out.println(product);
        }
    }
分页查询
    @Test
    public void searchDocumentPage() {
    
    
        // 1.确定查询方式
        MatchAllQueryBuilder builder = QueryBuilders.matchAllQuery();
        // 2.构建查询条件
        // 分页条件
        Pageable pageable = PageRequest.of(0, 3);
        NativeSearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(builder)
                .withPageable(pageable)
                .build();
        // 3.查询
        SearchHits<Product> result =
                template.search(query, Product.class);
        // 4.将查询结果封装为Page对象
        List<Product> content = new ArrayList();
        for (SearchHit<Product> productSearchHit : result) {
    
    
            Product product = productSearchHit.getContent();
            content.add(product);
        }
        /**
         * 封装Page对象,参数1:具体数据,参数2:分页条件对象,参数3:总条数
         */
        Page<Product> page = new PageImpl(content, pageable, result.getTotalHits());
        System.out.println(page.getTotalElements());
        System.out.println(page.getTotalPages());
        System.out.println(page.getContent());
    }
结果排序
    @Test
    public void searchDocumentSort() {
    
    
        // 1.确定查询方式
        MatchAllQueryBuilder builder = QueryBuilders.matchAllQuery();
        // 2.构建查询条件
        // 排序条件
        SortBuilder sortBuilder = SortBuilders.fieldSort("id").order(SortOrder.DESC);
        NativeSearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(builder)
                .withSorts(sortBuilder)
                .build();
        // 3.查询
        SearchHits<Product> result = template.search(query, Product.class);
        // 4.处理查询结果
        for (SearchHit<Product> productSearchHit : result) {
    
    
            Product product = productSearchHit.getContent();
            System.out.println(product);
        }
    }

四、Elasticsearch优化

磁盘选择

ES的优化即通过调整参数使得读写性能更快磁盘通常是服务器的瓶颈。Elasticsearch重度使用磁盘,磁盘的效率越高,Elasticsearch的执行效率就越高。这里有一些优化磁盘的技巧:

  1. 使用SSD(固态硬盘),它比机械磁盘优秀多了。
  2. 使用RAID0模式(将连续的数据分散到多个硬盘存储,这样可以并行进行IO操作),代价是一块硬盘发生故障就会引发系统故障。
  3. 不要使用远程挂载的存储。

分片策略

分片和副本数并不是越多越好。每个分片的底层都是一个Lucene索引,会消耗一定的系统资源。且搜索请求需要命中索引中的所有分片,分片数过多会降低搜索性能。索引的分片数需要架构师和技术人员对业务的增长有预先的判断,一般来说我们遵循以下原则:

  1. 每个分片占用的硬盘容量不超过ES的最大JVM的堆空间设置(一般设置不超过32G)。比如:如果索引的总容量在500G左右,那分片数量在16个左右即可。
  2. 分片数一般不超过节点数的3倍。比如:如果集群内有10个节点,则分片数不超过30个。
  3. 减少副本数量:进行写入操作时,需要把写入的数据都同步到副本,副本越多写入的效率就越慢。我们进行大批量进行写入操作时可以先设置副本数为0,写入完成后再修改回正常的状态。
  4. 推迟分片分配:节点中断后集群会重新分配分片。但默认集群会等待一分钟来查看节点是否重新加入。我们可以设置等待的时长,减少重新分配的次数:
PUT  /索引/_settings
{
    
    
    "settings":{
    
    
      "index.unassianed.node_left.delayed_timeout":"5m"
   }
}

内存设置

ES默认占用内存是4GB,我们可以修改config/jvm.option设置ES的堆内存大小,Xms表示堆内存的初始大小,Xmx表示可分配的最大内存。

  • Xmx和Xms的大小设置为相同的,可以减轻伸缩堆大小带来的压力。
  • Xmx和Xms不要超过物理内存的50%,因为ES内部的Lucene也要占据一部分物理内存。
  • Xmx和Xms不要超过32GB,由于Java语言的特性,堆内存超过32G会浪费大量系统资源,所以在内存足够的情况下,最终我们都会采用设置为31G:
cd /usr/local/elasticsearch1/config/
vim jvm.option

-Xms 31g
-Xmx 31g

五、Elasticsearch案例

  • 创建索引
PUT /news
{
    
    
  "settings": {
    
    
    "number_of_shards": 5,
    "number_of_replicas": 1,
    "analysis": {
    
    
      "analyzer": {
    
    
        "ik_pinyin": {
    
    
          "tokenizer": "ik_smart",
          "filter": "pinyin_filter"
        },
        "tag_pinyin": {
    
    
          "tokenizer": "keyword",
          "filter": "pinyin_filter"
        }
      },
      "filter": {
    
    
        "pinyin_filter": {
    
    
          "type": "pinyin",
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "remove_duplicated_term": true
        }
      }
    }
  },
  "mappings": {
    
    
    "properties": {
    
    
      "id": {
    
    
        "type": "integer",
        "index": true
      },
      "title": {
    
    
        "type": "text",
        "index": true,
        "analyzer": "ik_pinyin",
        "search_analyzer": "ik_smart"
      },
      "content": {
    
    
        "type": "text",
        "index": true,
        "analyzer": "ik_pinyin",
        "search_analyzer": "ik_smart"
      },
      "url": {
    
    
        "type": "keyword",
        "index": true
      },
      "tags": {
    
    
        "type": "completion",
        "analyzer": "tag_pinyin",
        "search_analyzer": "tag_pinyin"
      }
    }
  }
}

mysql数据同步到es中

①、解压logstash-7.17.0-windows-x86_64.zip

windows:logstash要和elastisearch版本一致

②、在解压路径下的/config中创建mysql.conf文件,文件写入以下脚本内容:

input {
    
    
    jdbc {
    
    
        jdbc_driver_library => "C:\Users\Administrator\Desktop\logstash-7.17.0-windows-x86_64\mysql-connector-java-8.0.29.jar"
        jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
        jdbc_connection_string => "jdbc:mysql:///news"
        jdbc_user => "root"
        jdbc_password => "root"
        schedule => "* * * * *"
        jdbc_default_timezone => "Asia/Shanghai"
        statement => "SELECT * FROM news"
    }
}

filter {
    
    
	mutate {
    
    
		split => {
    
    "tags" => ","}
	}
}

output {
    
    
    elasticsearch {
    
    
		hosts => ["192.168.66.66:9200"]
        index => "news"
        document_id => "%{id}"
    }
}

③、在解压路径下打开cmd黑窗口,运行命令:cmd

bin\logstash -f config\mysql.conf

④、测试

GET /news/_search
{
    
    
  "query": {
    
    
    "match_all": {
    
    }
  }
}

猜你喜欢

转载自blog.csdn.net/qq_56571862/article/details/128850291