前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Elasticsearch学习(六)手把手教你用Java操作Elaticsearch, 教你学会ElasticsearchTemplate的使用

Elasticsearch学习(六)手把手教你用Java操作Elaticsearch, 教你学会ElasticsearchTemplate的使用

作者头像
一写代码就开心
发布2021-03-02 14:46:44
1.7K0
发布2021-03-02 14:46:44
举报
文章被收录于专栏:java和python

Spring Data Elasticsearch

使用Spring Data 下二级子项目Spring Data Elasticsearch进行操作。支持POJO方法操作Elasticsearch。相比Elasticsearch提供的API更加简单更加方便。

Spring Data Elasticsearch项目环境搭建

创建项目

以上项目是一个空项目,什么依赖都没有添加

添加依赖

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com</groupId>
    <artifactId>SpringDateEs</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>


    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>6.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

其中和es相关的就是一个依赖,以后在项目里面想要使用java操作es,那么就添加这个依赖就可以了

代码语言:javascript
复制
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

编写配置文件

代码语言:javascript
复制
spring:
  data:
    elasticsearch:
      cluster-name: docker-cluster
      cluster-nodes: 192.168.40.145:9300

配置文件中cluster-name的值如何而来?这个是es集群的名字 浏览器输入地址之后就出现了

cluster-nodes 的值就是es在哪个虚拟机上面,那么就是哪个虚拟机的ip地址,我们创建es容器的时候,有两个端口号,一个是9200,一个是9300,9200是浏览器访问es的端口号,9300是java项目访问es需要写的端口号,所以,在我们的项目里面,需要写的是9300

在测试类里面写测试的方法来操作es

代码语言:javascript
复制
  @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

只要注入以上的这个,那么就可以使用elasticsearchTemplate操作es了

ElasticsearchTemplate的使用

1 创建实体

代码语言:javascript
复制
@Document指定实体类和索引对应关系

indexName:索引名称    写了这个之后,有就用,没有就创建这个索引
type: 索引类型
shards: 主分片数量
replicas:复制分片数量

@Id 指定主键
@Field指定普通属性

type: 对应Elasticsearch中属性类型。使用FiledType枚举可以快速获取。测试发现没有type属性可能出现无法自动创建类型问题,所以一定要有type属性。
text类型能被分词
keywords不能被分词


index: 是否创建索引。作为搜索条件时index必须为true
analyzer:指定分词器类型。


@Document(indexName = "people_index",type = "people_type",shards = 1,replicas = 1)
public class People {
    @Id
    private String id;
    // 整个name不被分词,切不创建索引
    // Keyword表示不被分词
    @Field(type= FieldType.Keyword,index = false)
    private String name;
    // address被ik分词
    // Text类型的属性才能被分词
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String address;

    @Field(type = FieldType.Long,index = false)
    private int age;

2 初始化索引(相当于创建数据库)

根据实体类上面的注解写的东西,我们要创建索引,相当于我们要创建一个数据库,在测试类里面写:

代码语言:javascript
复制
elasticsearchTemplate有很多的方法,其中createIndex()就是创建一个索引,只是创建一个数据库,里面的字段是什么类型,也就是在实体类里面的注解上面的那些字段的类型如何放到索引中,需要putMapping()方法。

也就是在创建索引的时候,createIndex(),putMapping()  都是要有的,只要写了这两个方法,那么在es里面就创建了一个索引了
代码语言:javascript
复制
    @Test
    void contextLoads() {
//        根据实体类创建索引,
        boolean result1 = elasticsearchTemplate.createIndex(People.class);
        System.out.println(result1);
//        将索引放到软件里面
        boolean results = elasticsearchTemplate.putMapping(People.class);
        System.out.println(results);
    }

以上在实体里面的注解里面的索引的 名字是people_index_16 所以我们现在要创建的索引,执行代码

他们返回的值是

返回true,说明创建索引成功,我们可以在kibana里面查看

有这个索引,相当于有数据库了,只是里面没有数据

3 删除索引(删除数据库)

代码语言:javascript
复制
@Test
void delete(){
    boolean result = elasticsearchTemplate.deleteIndex(People.class);
    System.out.println(result);
}

执行完上面的代码,我们在kibana里面查看索引在不在了?

相当于删除数据库了

4 添加文档 (往数据库添加数据)

如果索引和类型不存在,也可以执行进行新增,新增后自动创建索引和类型。但是field通过动态mapping进行映射,elaticsearch根据值类型进行判断每个属性类型,默认每个属性都是standard分词器,ik分词器是不生效的。所以一定要先通过代码进行初始化或直接在elasticsearch中通过命令创建所有field的mapping

4.1 新增单个文档(新增一条数据)

如果对象的id属性没有赋值,让ES自动生成主键,存储时id属性没有值,_id存储document的主键值。 如果对象的id属性明确设置值,存储时id属性为设置的值,ES中document对象的_id也是设置的值。

代码语言:javascript
复制
@Test
void insert1(){

创建要往es中添加的数据的实体类
    People peo = new People();
    peo.setId("123");
    peo.setName("张三");
    peo.setAddress("北京市海淀区回龙观东大街");
    peo.setAge(18);



    IndexQuery query = new IndexQuery();
    query.setObject(peo);
    
index() 方法就是新增的方法,只是里面的参数的类型是IndexQuery,所以要创建IndexQuery对象

    String result = elasticsearchTemplate.index(query);
    System.out.println(result);
}

新增成功,返回的是数据的id值

我们从kibana里面看看有没有新增数据

4.2 批量新增(新增多条数据)

下面代码中使用的IndexQueryBuilder()进行构建,可以一行代码完成。也可以使用上面的IndexQuery()。效果是完全相同的,只是需要写多行。

代码语言:javascript
复制
@Test
void bulk(){

    List<IndexQuery> list = new ArrayList<>();

    // IndexQuery多行写法
    IndexQuery indexQuery = new IndexQuery();
    indexQuery.setObject(new People("1","王五","北京东城",12));
    list.add(indexQuery);

    // IndexQuery 连缀写法
    list.add(new IndexQueryBuilder().withObject(new People("2","赵六","北京西城",13)).build());

    list.add(new IndexQueryBuilder().withObject(new People("3","吴七","北京昌平",14)).build());

    elasticsearchTemplate.bulkIndex(list);
}

执行完上面的代码,我们看看kibana里面

以上es里面确实有很多的数据了。

5 删除操作(删除数据)

根据主键删除

代码语言:javascript
复制
delete(String indexName,String typeName,String id); 通过字符串指定索引,类型和id值
delete(Class,String id)
 第一个参数传递实体类类类型,建议使用此方法,
减少索引名和类型名由于手动编写出现错误的概率。

返回值为delete方法第二个参数值(删除文档的主键值)

代码语言:javascript
复制
@Test
void deleteDoc(){
删除id为4的数据
    String result = elasticsearchTemplate.delete(People.class, "4");
    System.out.println(result);
}

按照条件删除

代码语言:javascript
复制
DeleteQuery deleteQuery = new DeleteQuery();
deleteQuery.setQuery(new MatchQueryBuilder("desc","学生"));
elasticsearchTemplate.delete(deleteQuery,People.class);

6 修改操作(修改数据)

修改操作就是新增代码,只要保证主键id已经存在,新增就是修改

7 查询操作(查询数据)

7.1 模糊查询

去所有field中查询指定条件。

代码语言:javascript
复制
@Test
void query(){
    // NativeSearchQuery构造方法参数。
    // 北京去和所有field进行匹配,只要出现了北京就可以进行查询
    QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery("北京");
    // 查询条件SearchQuery是接口,只能实例化实现类。
    SearchQuery searchQuery = new NativeSearchQuery(queryStringQueryBuilder);
    List<People> list = elasticsearchTemplate.queryForList(searchQuery, People.class);
    for(People people : list){
        System.out.println(people);
    }
}

7.2 使用match_all查询所有文档

代码语言:javascript
复制
@Test
void matchAll(){
    SearchQuery searchQuery = new NativeSearchQuery(QueryBuilders.matchAllQuery());
    List<People> list = elasticsearchTemplate.queryForList(searchQuery, People.class);
    for(People people : list){
        System.out.println(people);
    }
}

7.3 使用match查询文档

代码语言:javascript
复制
@Test
void match(){
    SearchQuery searchQuery = new NativeSearchQuery(QueryBuilders.matchQuery("address","我要去北京"));
    List<People> list = elasticsearchTemplate.queryForList(searchQuery, People.class);
    for(People people : list){
        System.out.println(people);
    }
}

7.4使用match_phrase查询文档

短语搜索是对条件不分词,但是文档中属性根据配置实体类时指定的分词类型进行分词。 如果属性使用ik分词器,从分词后的索引数据中进行匹配。

代码语言:javascript
复制
@Test
void mathPhrase(){
    SearchQuery searchQuery = new NativeSearchQuery(QueryBuilders.matchPhraseQuery("address","北京市"));
    List<People> list = elasticsearchTemplate.queryForList(searchQuery, People.class);
    for(People people : list){
        System.out.println(people);
    }
}

7.5使用range查询文档

代码语言:javascript
复制
@Test
void range(){
    SearchQuery searchQuery = new NativeSearchQuery(QueryBuilders.rangeQuery("age").gte(22).lte(23));
    List<People> list = elasticsearchTemplate.queryForList(searchQuery, People.class);
    for(People people : list){
        System.out.println(people);
    }
}

7.6多条件查询

代码语言:javascript
复制
  @Test
    void MustShould(){
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        
        List<QueryBuilder> listQuery = new ArrayList<>();
        
        listQuery.add(QueryBuilders.matchPhraseQuery("name","张三"));
        listQuery.add(QueryBuilders.rangeQuery("age").gte(22).lte(23));
        
//        boolQueryBuilder.should().addAll(listQuery); // 逻辑或
        boolQueryBuilder.must().addAll(listQuery);  // 逻辑与
        
        SearchQuery searchQuery = new NativeSearchQuery(boolQueryBuilder);
        List<People> list = elasticsearchTemplate.queryForList(searchQuery, People.class);
        for(People people : list){
            System.out.println(people);
        }
    }

7.7分页与排序

代码语言:javascript
复制
@Test
void PageSort(){
    SearchQuery searchQuery = new NativeSearchQuery(QueryBuilders.matchAllQuery());
    // 分页 第一个参数是页码,从0算起。第二个参数是每页显示的条数
    searchQuery.setPageable(PageRequest.of(0,2));
    // 排序 第一个参数排序规则 DESC ASC 第二个参数是排序属性
    searchQuery.addSort(Sort.by(Sort.Direction.DESC,"age"));
    List<People> list = elasticsearchTemplate.queryForList(searchQuery, People.class);
    for(People people : list){
        System.out.println(people);
    }
}

如果实体类中主键只有@Id注解,String id对应ES中是text类型,text类型是不允许被排序,所以如果必须按照主键进行排序时需要在实体类中设置主键类型

代码语言:javascript
复制
@Id
@Field(type = FieldType.Keyword)
private String id;

7.8高亮查询

代码语言:javascript
复制
    @Test
    void hl() {

创建高亮的实体类对象

        // 高亮属性
        HighlightBuilder.Field hf = new HighlightBuilder.Field("address");
        // 高亮前缀
        hf.preTags("<span>");
        // 高亮后缀
        hf.postTags("</span>");



创建查询对象
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                // 排序
                .withSort(SortBuilders.fieldSort("age").order(SortOrder.DESC))
                // 分页
                .withPageable(PageRequest.of(0, 2))
                // 应用高亮
                .withHighlightFields(hf)
                // 查询条件, 必须要有条件,否则高亮
                .withQuery(QueryBuilders.matchQuery("address", "北京市")).build();



第三个参数    new SearchResultMapper()   就是自定义映射的结果



AggregatedPage<People> peoples = elasticsearchTemplate.queryForPage(searchQuery, People.class,


 new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                /*
                {
  "took" : 190,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.7918377,
    "hits" : [
      {
        "_index" : "people_index",
        "_type" : "people_type",
        "_id" : "1",
        "_score" : 1.7918377,
        "_source" : {
          "id" : "1",
          "name" : "张三",
          "address" : "北京市回龙观东大街",
          "age" : 21
        },
        "highlight" : {
          "address" : [
            "<span>北京</span><span>市</span>回龙观东大街"
          ]
        }
      },
      {
        "_index" : "people_index",
        "_type" : "people_type",
        "_id" : "2",
        "_score" : 1.463562,
        "_source" : {
          "id" : "2",
          "name" : "李四",
          "address" : "北京市大兴区科创十四街",
          "age" : 22
        },
        "highlight" : {
          "address" : [
            "<span>北京</span><span>市</span>大兴区科创十四街"
          ]
        }
      },
      {
        "_index" : "people_index",
        "_type" : "people_type",
        "_id" : "3",
        "_score" : 0.38845786,
        "_source" : {
          "id" : "3",
          "name" : "王五",
          "address" : "天津市北京大街",
          "age" : 23
        },
        "highlight" : {
          "address" : [
            "天津市<span>北京</span>大街"
          ]
        }
      }
    ]
  }
}

                 */

                // 取最里面层的hits
                SearchHit[] searchHits = searchResponse.getHits().getHits();
                // 最终返回的数据
                List<T> peopleList = new ArrayList<>();
                for (SearchHit searchHit : searchHits) {
                    //需要把SearchHit转换为People
                    People peo = new People();
                    // 取出非高亮数据
                    Map<String, Object> mapSource = searchHit.getSourceAsMap();
                    // 取出高亮数据
                    Map<String, HighlightField> mapHL = searchHit.getHighlightFields();
                    // 把非高亮数据进行填充
                    peo.setId(mapSource.get("id").toString());
                    peo.setName(mapSource.get("name").toString());
                    peo.setAge(Integer.parseInt(mapSource.get("id").toString()));
                    // 判断是否有高亮,如果只有一个搜索条件,一定有这个高亮数据,if可以省略
                    if (mapHL.containsKey("address")) {
                        // 设置高亮数据
                        peo.setAddress(mapHL.get("address").getFragments()[0].toString());
                    }
                    // 把people添加到集合中
                    peopleList.add((T) peo);
                }
                // 如果没有分页,只有第一个参数。
                // 总条数在第一个hits里面。
                return new AggregatedPageImpl<>(peopleList, pageable, searchResponse.getHits().totalHits);
            }

            @Override
            public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
                return null;
            }
        });

        // 通过getContent查询出最终List<People>
        for (People people : peoples.getContent()) {
            System.out.println(people);
        }
    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring Data Elasticsearch
  • Spring Data Elasticsearch项目环境搭建
    • 创建项目
      • 添加依赖
        • 编写配置文件
          • 在测试类里面写测试的方法来操作es
          • ElasticsearchTemplate的使用
            • 1 创建实体
              • 2 初始化索引(相当于创建数据库)
                • 3 删除索引(删除数据库)
                  • 4 添加文档 (往数据库添加数据)
                    • 4.1 新增单个文档(新增一条数据)
                    • 4.2 批量新增(新增多条数据)
                  • 5 删除操作(删除数据)
                    • 6 修改操作(修改数据)
                      • 7 查询操作(查询数据)
                        • 7.1 模糊查询
                        • 7.2 使用match_all查询所有文档
                        • 7.3 使用match查询文档
                        • 7.4使用match_phrase查询文档
                        • 7.5使用range查询文档
                        • 7.6多条件查询
                        • 7.7分页与排序
                        • 7.8高亮查询
                    相关产品与服务
                    Elasticsearch Service
                    腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档