SpringData ElasticSearch


在这里插入图片描述

想学习架构师构建流程请跳转:Java架构师系统架构设计

1 SpringData ElasticSearch简介

Elasticsearch是一个实时的分布式搜索和分析引擎。它底层封装了Lucene框架,可以提供分布式多用户的全文搜索服务。

Spring Data ElasticSearch是SpringData技术对ElasticSearch原生API封装之后的产物,它通过对原生API的封装,使得程序员可以简单的对ElasticSearch进行各种操作。

2 ElasticSearch环境搭建

2.1 安装ElasticSearch

2.1.1 准备工作

  1. 修改进程限制,编辑/etc/security/limits.conf,添加下面的代码
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096

在这里插入图片描述

  1. 修改系统变量的最大值,编辑/etc/sysctl.conf,添加下面的配置
vm.max_map_count = 655360

保存修改之后,执行 sysctl -p 命令

[root@localhost ~]# sysctl -p 
vm.max_map_count = 655360

2.1.2 文件上传

上传文件到src目录下

[root@localhost src]# ls
elasticsearch-5.6.8.tar.gz  redis-5.0.4  redis-5.0.4.tar.gz

2.1.3 文件解压

直接将软件解压到安装目录

[root@localhost src]# tar -zxvf elasticsearch-5.6.8.tar.gz -C /usr/local
[root@localhost src]# cd /usr/local/elasticsearch-5.6.8
[root@localhost elasticsearch-5.6.8]# ls
bin  config  lib  LICENSE.txt  modules  NOTICE.txt  plugins  README.textile

2.1.4 添加用户

新增加一个es用户,并将elasticsearch-5.6.8目录的所属用户和用户组改成es

[root@localhost elasticsearch-5.6.8]# useradd es
[root@localhost elasticsearch-5.6.8]# chown es:es -R ../elasticsearch-5.6.8
[root@localhost elasticsearch-5.6.8]# ll
drwxr-xr-x. 8 es   es         143 516 04:08 elasticsearch-5.6.8

切换到新创建的es用户,执行后续操作

[root@localhost ~]# su - es
[es@localhost ~]$ 

2.1.5 修改配置

编辑配置文件,修改数据文件和日志文件的存储位置以及es的绑定地址

[es@localhost ~]$ cd /usr/local/elasticsearch-5.6.8/config/
[es@localhost config]$ ls
elasticsearch.yml  jvm.options  log4j2.properties  scripts
[es@localhost config]$ vim elasticsearch.yml

在这里插入图片描述

2.1.6 启动elasticSearch

[es@localhost bin]$ cd /usr/local/elasticsearch-5.6.8/bin/
[es@localhost bin]$ nohup ./elasticsearch &
[1] 19645

2.1.7 访问测试

通过服务器的9200端口访问,得到下面的结果,证明安装成功.

在这里插入图片描述

2.2 安装Head插件

windows版本:
地址:https://github.com/liufengji/es-head

安装插件: google —》更多工具----》扩展程序
将解压的elasticsearch-head.crx文件拖进来。
提示程序包无效:“CRX_HEADER_INVALID”
将elasticsearch-head.crx更名为elasticsearch-head.rar 再解压;
进入elasticsearch-head文件夹将_metadata文件夹重命名为metadata
打开Google的扩展程序,点击加载已解压的扩展程序,选择解压elasticsearch-head文件夹即可添加插件成功
在这里插入图片描述

2.2.1 安装nodeJS

将nodeJS的安装包上传到/usr/local/src下,然后解压到/usr/local下,然后将npm和node建立软连接到

/usr/local/bin/下

[root@localhost src]# tar -zxvf node-v10.16.0-linux-x64.tar.gz -C /usr/local/
[root@localhost src]# ln -s /usr/local/node-v10.16.0-linux-x64/bin/npm /usr/local/bin/ 
[root@localhost src]# ln -s /usr/local/node-v10.16.0-linux-x64/bin/node /usr/local/bin/ 
[root@localhost src]# node -v
v10.16.0

2.2.2 安装cnpm

[root@localhost src]# npm install -g cnpm --registry=https://registry.npm.taobao.org
[root@localhost src]# ln -s /usr/local/node-v10.16.0-linux-x64/bin/cnpm /usr/local/bin/
[root@localhost src]# cnpm -v

2.2.3 安装grunt

[root@localhost src]# npm install -g grunt-cli
[root@localhost src]# ln -s /usr/local/node-v10.16.0-linux-x64/bin/grunt /usr/local/bin

2.2.4 安装head插件

上传head插件到/usr/local/src/下,然后解压到/usr/local下

[root@localhost src]# unzip elasticsearch-head-master.zip
[root@localhost src]# cp -R elasticsearch-head-master /usr/local/

2.2.5 安装head插件所需依赖

[root@localhost src]# cd /usr/local/elasticsearch-head-master
[root@localhost elasticsearch-head-master]# cnpm install

2.2.6 修改elasticsearch的配置

编辑配置文件:/usr/local/elasticsearch-5.6.8/config/elasticsearch.yml,添加跨域请求允许,即增加以下两行:

http.cors.enabled: true
http.cors.allow-origin: "*"

修改完毕之后,要对elasticsearch进行重启

2.2.7 启动head

在head目录下启动插件

[root@localhost elasticsearch-head-master]# grunt server &

2.2.8 访问测试

通过服务器的9100端口访问,得到下面的结果,证明安装成功.

在这里插入图片描述

2.3 安装IK分词器

2.3.1 说明

ES默认的中文分词器是将每一个汉字作为一个词,这显然不合适,而IK分词是一款国人开发的相对简单的中文分词器,它包含大量的中文词,而且支持自定义分词。

ik分词器提供的分词规则:

  • ik_max_word:会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。
  • ik_smart:会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂

两种分词器使用的最佳实践是:索引时用ik_max_word,在搜索时用ik_smart。即:索引时最大化的将文章内容分词,搜索时更精确的搜索到想要的结果。

2.3.2 安装

第一步:下载得到ik分词器的安装包,将其解压得到有一个文件夹,并将文件夹重命名为ik

[root@localhost src]# unzip elasticsearch-analysis-ik-5.6.8.zip
[root@localhost src]# ls
elasticsearch

第二步:将elastaicsearch文件夹拷贝到elastaicsearch-5.6.8下的plugins目录下,并重命名为ik

[root@localhost src]# cp -r elasticsearch /usr/local/elasticsearch-5.6.8/plugins/ik

第三步:重新启动elasticsearch即可加载IK分词器

第四部:测试

http://服务地址:9200/_analyze?analyzer=ik_smart&pretty=true&text=我是程序员

在这里插入图片描述

3 ElasticSearch基础知识回顾

3.1 核心概念

3.1.1 索引 index

一个索引就是一个拥有几分相似特征的文档的集合。索引就类似于关系型数据库中的库的概念

3.1.2 类型 type

一个类型是索引中的一个逻辑上的分类/分区。类型就类似于关系型数据库中的数据表的概念。

3.1.3 映射 mapping

映射是对类型中的字段的限制。映射就类似于关系型数据库中的数据表结构的概念

3.1.4 文档 document

一个文档是一个可被索引的基础信息单元。文档就类似于关系型数据库中的行的概念

ElasticSearch跟关系型数据库中概念的对比:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields

在这里插入图片描述

主键_id和文档内容是一套的key-value形式.删除的时候根据_id主键进行删除.

3.2 常见操作

3.2.1 创建工程,引入坐标

<dependencies>
    <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>5.6.8</version>
    </dependency>
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>transport</artifactId>
        <version>5.6.8</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-to-slf4j</artifactId>
        <version>2.9.1</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.24</version>
    </dependency>
</dependencies>

3.2.2 索引操作

public class EsTest {
    TransportClient client = null;

    //创建连接
    @Before
    public void initClient() {
        try {
            //创建连接
            client = new PreBuiltTransportClient(Settings.EMPTY).
                    addTransportAddress(
                            new InetSocketTransportAddress(
                                    InetAddress.getByName("192.168.106.128"), 9300));
        } catch (UnknownHostException e) {
            System.out.println("创建client失败");
            e.printStackTrace();
        }
    }

    //关闭连接
    @After
    public void closeClient() {
        client.close();
    }

    //创建索引
    @Test
    public void testCreateIndex() {
        client.admin().indices().prepareCreate("oldlu").get();
    }


    //删除索引
    @Test
    public void testDeleteIndex() {
        client.admin().indices().prepareDelete("oldlu").get();
    }
}

3.2.3 映射操作

public class EsTest {
    //创建映射
    @Test
    public void testCreateMappping() throws Exception {
        XContentBuilder builder = XContentFactory.jsonBuilder()
                .startObject()
                .startObject("article")
                .startObject("properties")
                .startObject("title")
                .field("type", "string")
                .field("store", "yes")
                .field("analyzer", "ik_smart")
                .endObject()
                .startObject("content")
                .field("type", "string")
                .field("store", "yes")
                .field("analyzer", "ik_smart")
                .endObject()
                .startObject("hits")
                .field("type", "long")
                .field("store", "yes")
                .endObject()
                .endObject()
                .endObject()
                .endObject();

        // 创建映射(表结构)
        PutMappingRequest mapping = Requests.putMappingRequest("oldlu")//指定索引(库)
                .type("article")//指定类型(表)
                .source(builder);
        client.admin().indices().putMapping(mapping).get();
    }
}

3.2.4 文档增删改操作

public class EsTest {
    //创建文档
    @Test
    public void testCreateDocuments() {
        //封装数据
        Map<String, Object> map = new HashMap<>();
        map.put("title", "oldlu");
        map.put("content", "oldlu很低调");
        map.put("hits", 10);

        client.prepareIndex("oldlu", "article", "1").setSource(map).get();
    }

    //修改文档
    @Test
    public void testUpdateDocuments() {
        //封装Map
        Map<String, Object> map = new HashMap<>();
        map.put("title", "传智播客");
        map.put("content", "传智播客很低调");
        map.put("hits", 12);

        client.prepareUpdate("oldlu", "article", "1").setDoc(map).get();
    }

    //删除文档
    @Test
    public void testDeleteDocuments() {
        //删除文档
        client.prepareDelete("oldlu", "article", "1").get();
    }
}

3.2.5 文档查询操作

public class EsTest {
    //查询所有
    @Test
    public void testFindAll() throws Exception {
        //设置搜索条件
        SearchResponse searchResponse = client.prepareSearch("oldlu")
                .setTypes("article")
                .setQuery(QueryBuilders.matchAllQuery())//查询所有
                .get();

        //获取命中次数,查询结果有多少对象
        SearchHits hits = searchResponse.getHits();
        System.out.println("查询结果有:" + hits.getTotalHits() + "条");

        //遍历搜索结果数据
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString());
        }
    }

    //条件查询
    @Test
    public void testFindByTitle() throws Exception {

        //设置搜索条件
        SearchResponse searchResponse = client.prepareSearch("oldlu")
                .setTypes("article")
                .setQuery(QueryBuilders.termQuery("title", "程序员"))//具体条件
                .get();

        //获取命中次数,查询结果有多少对象
        SearchHits hits = searchResponse.getHits();
        System.out.println("查询结果有:" + hits.getTotalHits() + "条");

        //遍历搜索结果数据
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString());
        }
    }

    //分页排序查询
    @Test
    public void testSortAndPage() throws Exception {

        //搜索数据
        SearchResponse searchResponse = client.prepareSearch("oldlu")
                .setTypes("article")
                .setQuery(QueryBuilders.matchAllQuery())
                .setFrom(0).setSize(2)//分页条件
                .addSort("hits", SortOrder.DESC)//排序
                .get();

        //获取命中次数,查询结果有多少对象
        SearchHits hits = searchResponse.getHits();
        System.out.println("查询结果有:" + hits.getTotalHits() + "条");

        //遍历搜索结果数据
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString()); // 获取字符串格式打印
        }
    }
}

4 SpringData ElasticSearch入门案例

4.1 目标

通过SpringData ES技术向ElasticSearch数据库存储一条数据

4.2 创建工程,引入坐标

 <dependencies>
     <dependency>
         <groupId>org.elasticsearch</groupId>
         <artifactId>elasticsearch</artifactId>
         <version>5.6.8</version>
     </dependency>
     <dependency>
         <groupId>org.elasticsearch.client</groupId>
         <artifactId>transport</artifactId>
         <version>5.6.8</version>
     </dependency>
     <dependency>
         <groupId>org.springframework.data</groupId>
         <artifactId>spring-data-elasticsearch</artifactId>
         <version>3.0.5.RELEASE</version>
     </dependency>

     <dependency>
         <groupId>org.apache.logging.log4j</groupId>
         <artifactId>log4j-to-slf4j</artifactId>
         <version>2.9.1</version>
     </dependency>
     <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-api</artifactId>
         <version>1.7.24</version>
     </dependency>
     <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <version>1.2.12</version>
     </dependency>

     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-test</artifactId>
         <version>5.1.6.RELEASE</version>
     </dependency>
</dependencies>

4.3 添加配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
       xsi:schemaLocation="http://www.springframework.org/schema/data/elasticsearch
       http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 扫描dao包 -->
    <elasticsearch:repositories base-package="net.csdn"/>

    <!-- 配置Client -->
    <elasticsearch:transport-client id="client" cluster-nodes="192.168.106.128:9300"/>

    <!-- 配置搜索模板  -->
    <bean id="elasticsearchTemplate"
          class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
        <constructor-arg name="client" ref="client"/>
    </bean>
</beans>

4.4 创建实体类

//indexName: 索引名称   type: 类型
@Document(indexName = "oldlu-sd", type = "article")
public class Article {
    @Id
    @Field(type = FieldType.Long)
    private Integer id;

    /**
     * index:是否设置分词
     * analyzer:存储时使用的分词器
     * searchAnalyze:搜索时使用的分词器
     * store:是否存储
     * type: 数据类型
     */
    @Field(index = true, analyzer = "ik_smart", searchAnalyzer = "ik_smart", 
           				 store = true, type = FieldType.text)
    private String title;

    @Field(index = true, analyzer = "ik_smart", searchAnalyzer = "ik_smart", 
           				 store = true, type = FieldType.text)
    private String content;

	//省略set get  toString方法
}

4.5 自定义dao接口

//自定义dao接口继承ElasticsearchRepository<实体类型,主键类型>
public interface ArticleDao extends ElasticsearchRepository<Article,Integer> {}

4.6 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ArticleDaoTest {

    @Autowired
    ElasticsearchTemplate esTemplate;
    
    @Autowired
    private ArticleDao articleDao;
    
    @Test
    public void testSave() {
        
        //创建索引
        esTemplate.createIndex(Article.class);
		
        //创建映射
        esTemplate.putMapping(Article.class);
        
        //保存数据
        Article article = new Article();
        article.setId(10);
        article.setTitle("oldlu10");
        article.setContent("oldlu很低调10");
        articleDao.save(article);
    }
}

5 SpringData ElasticSearch实现CRUD操作

5.1 增删改

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ArticleDaoTest {
    @Autowired
    private ArticleDao articleDao;

    //新增
    @Test
    public void testSave() {
        Article article = new Article();
        article.setId(11);
        article.setTitle("oldlu11");
        article.setContent("oldlu很低调11");
        articleDao.save(article);
    }
    
    //修改
    @Test
    public void testUpdate() {
        Article article = new Article();
        article.setTitle("oldlu11111");
        article.setContent("oldlu很低调1111");
        article.setId(11);
        
        //主键如果在数据库存在就更新  如果不存在就保存
        articleDao.save(article);
    }
    
    //删除
    @Test
    public void testdelete() {
        articleDao.deleteById(11);
    }
}   

5.2接口方法查询

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ArticleDaoTest {
    @Autowired
    private ArticleDao articleDao;
    
    //查询所有
    @Test
    public void testFindAll(){
        Iterable<Article> articles = articleDao.findAll();
        for (Article article : articles) {
            System.out.println(article);
        }
    }

    //根据主键查询
    @Test
    public void testFindByid() {
        Optional<Article> optional = articleDao.findById(16);
        System.out.println(optional.get());
    }
    
    //分页查询
    @Test
    public void testFindAllWithPage() {
        //从0开始查询,查10条
        Pageable pageable = PageRequest.of(0,10);
        Iterable<Article> articles = articleDao.findAll(pageable);
        for (Article article : articles) {
            System.out.println(article);
        }
    }

    //排序查询
    @Test
    public void testFindAllWithSort() {
        //从0开始查询,查10条
        Sort sort = Sort.by(Sort.Order.asc("id"));
        Iterable<Article> articles = articleDao.findAll(sort);
        for (Article article : articles) {
            System.out.println(article);
        }
    }

    //分页排序查询
    @Test
    public void testFindAllWithPageAndSort() {
        //从0开始查询,查10条
        Sort sort = Sort.by(Sort.Order.desc("id"));
        Pageable pageable = PageRequest.of(0, 10, sort);
        Iterable<Article> articles = articleDao.findAll(pageable);
        for (Article article : articles) {
            System.out.println(article);
        }
    }
}

5.3 命名规则查询

es的命名规则跟jpa基本一致,常见的如下:

关键字命名规则解释示例
andfindByField1AndField2根据Field1和Field2获得数据findByTitleAndContent
orfindByField1OrField2根据Field1或Field2获得数据findByTitleOrContent
isfindByField根据Field获得数据findByTitle
notfindByFieldNot根据Field获得补集数据findByTitleNot
betweenfindByFieldBetween获得指定范围的数据findByPriceBetween
lessThanEqualfindByFieldLessThan获得小于等于指定值的数据findBy

下面,我们在dao接口中按照规则进行自定义查询方法

//自定义dao接口继承ElasticsearchRepository<实体类型,主键类型>
public interface ArticleDao extends ElasticsearchRepository<Article,Integer> {
    //根据标题查询
    List<Article> findByTitle(String title);

    //根据标题或内容查询
    List<Article> findByTitleOrContent(String title, String content);

    //根据标题或内容查询(含分页)
    Page<Article> findByTitleOrContent(String title, String content, Pageable pageable);
}

添加测试方法

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ArticleDaoTest {
    @Autowired
    private ArticleDao articleDao;
    
    @Test
    public void testFindByTitle() {
        Iterable<Article> articles = articleDao.findByTitle("程序员");
        for (Article article : articles) {
            System.out.println(article);
        }
    }

    @Test
    public void findByTitleOrContent() {
        List<Article> articles = articleDao.findByTitleOrContent("程序员", "低调");
        for (Article article : articles) {
            System.out.println(article);
        }
    }

    @Test
    public void findByTitleOrContent2() {
        Pageable pageable = PageRequest.of(0,20);
        Page<Article> page = articleDao.findByTitleOrContent("程序员", "低调",pageable);
        for (Article article : page.getContent()) {
            System.out.println(article);
        }
    }
}

6 ElasticSearchTemplate和ElasticSearchRepository区别

ElasticSearchTemplate更多是对ESRepository的补充,里面提供了一些更底层的方法。
ElasticsearchRepository作为域类的存储库,因为它是类型化的。它扩展了存储库的Spring接口,因此可以作为其中之一使用。如果您习惯了Spring存储库,您会感到非常舒服的。

要开始将对象索引到Elasticsearch,只需向其添加@Document注释,并创建扩展ElasticsearchRepository的Repository接口。

可索引类:

@Document(
    indexName = "customers", 
    type = "customer", 
    shards = 1, 
    replicas = 0, 
    refreshInterval = "-1"
)
public class Customer {
    @Id
    private Long id;
    private String name;

    public Customer() { 
    }

    public Customer(String name) {
        this.name = name;
    }

    //Getters and setters omited
}

存放处:

public interface CustomerRepository 
    extends ElasticsearchRepository<Customer, Long>{
}

有了这个,您就可以开箱即用,进行CRUD操作、索引、搜索等常见操作。
另一方面,ElasticsearchTemplate是一个用于处理索引的ElasticSearch客户端,它不是键入的,也不是与您的域类相关的。它更强大,因为您可以执行许多存储库实现不可用的任务,比如删除索引或进行聚合搜索。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵广陆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值