spring + spring data elasticsearch+ elasticsearch 使用ElasticsearchRestTemplate高亮查询!

前言:
目前网络上的教程几乎都是使用es的transportClient,但是在es8开始就废弃transportClient了,据说是因为他们觉得效率低下。目前推荐使用restClient进行增删改查,但是出现很多坑,藉此记录一下学习过程中的各种问题。
jar版本:elasticsearch6.7.2 + spring data elasticsearch 3.2.3 + spring 5.1.0

目录

  • elasticsearch安装以及IK分词器安装(windows)
    • 1.下载安装elasticsearch6.7.2
    • 2.下载安装IK分词器
  • 使用spring data elasticsearch3.2.3操作es
    • 1.引入jar包
    • 2.编写配置文件
    • 3.编写实体类
    • 4.编写测试类

elasticsearch安装以及IK分词器安装(windows)

1.下载安装elasticsearch6.7.2

下载地址在这里:https://www.elastic.co/cn/downloads/past-releases#elasticsearch
其实就是个zip压缩包,解压即用。
解压完成后,双击bin目录下的elasticsearch.bat就可以启动了。默认端口9200相当于mysql的3306。
用浏览器访问 http://localhost:9200/ 即可确认是否成功。成功将会显示如下:

{
  "name" : "_EzXnij",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "vvhztAGdSYy-_I-4MOP8RQ",
  "version" : {
    "number" : "6.7.2",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "56c6e48",
    "build_date" : "2019-04-29T09:05:50.290371Z",
    "build_snapshot" : false,
    "lucene_version" : "7.7.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

但在这里,我遇到了一个小小的坑。

公司电脑是8g内存,启动了3个es之后直接卡死。这让我很莫名其妙。查阅资料,发现es由java编写,意味着会有jvm占用内存。于是我找了一下配置文件,发现需要在/config/jvm.options文件中更改jvm的最大内存和最小内存(默认都是1G,所以我开3个es直接卡死)。下面贴上该文件的部分配置:

## JVM configuration

################################################################
## IMPORTANT: JVM heap size
################################################################
##
## You should always set the min and max JVM heap
## size to the same value. For example, to set
## the heap to 4 GB, set:
##
## -Xms4g
## -Xmx4g
##
## See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html
## for more information
##
################################################################

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms256m
-Xmx256m

看到上面的两个256m了么,默认值是1g,修改为256m。

2.下载安装IK分词器

下载地址在这里:https://github.com/medcl/elasticsearch-analysis-ik/releases
下载之前请看说明信息:

IK version ES version
master 7.x -> master
6.x 6.x
5.x 5.x
1.10.6 2.4.6

因此我们要选择6.7.2版本的IK分词器。
下载完成后依然是个压缩包,解压后会出现一个文件夹,将这个文件夹重命名为ik,放入es目录中的plugins下。重新启动es。当控制台中出现 loaded plugin [analysis-ik] 时候(下面日志的最后一行),说明IK分词器被加载成功。

[2019-12-27T08:45:26,369][INFO ][o.e.p.PluginsService     ] [_EzXnij] loaded module [x-pack-rollup]
[2019-12-27T08:45:26,369][INFO ][o.e.p.PluginsService     ] [_EzXnij] loaded module [x-pack-security]
[2019-12-27T08:45:26,369][INFO ][o.e.p.PluginsService     ] [_EzXnij] loaded module [x-pack-sql]
[2019-12-27T08:45:26,369][INFO ][o.e.p.PluginsService     ] [_EzXnij] loaded module [x-pack-upgrade]
[2019-12-27T08:45:26,369][INFO ][o.e.p.PluginsService     ] [_EzXnij] loaded module [x-pack-watcher]
[2019-12-27T08:45:26,369][INFO ][o.e.p.PluginsService     ] [_EzXnij] loaded plugin [analysis-ik]

使用spring data elasticsearch3.2.3操作es

1.引入jar包

首先在代码中引入springdata elasticsearch3.2.3的jar包,以及es6.7.2的jar包(我使用maven引入)。
另外还需要引入spring等jar包,我使用5.1.0版本的spring

    <dependency>
      <groupId>org.elasticsearch</groupId>
      <artifactId>elasticsearch</artifactId>
      <version>6.7.2</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-elasticsearch</artifactId>
      <version>3.2.3.RELEASE</version>
      <exclusions>
        <exclusion>
          <groupId>org.elasticsearch</groupId>
          <artifactId>elasticsearch</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.elasticsearch.plugin</groupId>
      <artifactId>transport-netty4-client</artifactId>
      <version>6.7.2</version>
    </dependency>

2.编写配置文件

由于要将restTemplate交给spring保管,因此需要在spring核心配置文件中创建对象。



     


我在这里坑了很久,查了各种资料,几乎都是用的transportClient,所以启动项目总是报错。最开始使用ElasticsearchTemplate,需要注入transportClient,我在测试类的注入目标是ElasticsearchRestTemplate,因此启动总是报错。后来我将配置文件中的ElasticsearchTemplate换成了ElasticsearchRestTemplate,结果报错说创建该对象失败,因为注入的client不对。我一个想到的是,注入的对象应该是restClient而不是transportClient,但是我并不知道如何去创建restClient,于是写了个bean标签创建,发现仍然报错,后来也不知道怎么搞的,将id='client’换成了id=‘restClient’,并更换前面的声明为elasticsearch:rest-client就成功了。
另外这里需要对es的实体类进行扫描包,以标记这是es的document。

3.编写实体类

直接上代码:

@Document(indexName = "myindex")
public class Book implements Serializable {

    @Field(index = false,type = FieldType.Text)
    private String id;
    @Field(analyzer = "ik_max_word",type = FieldType.Text,store = true,searchAnalyzer = "ik_max_word")
    private String name;
    @Field(analyzer = "ik_max_word",type = FieldType.Text,store = true,searchAnalyzer = "ik_max_word")
    private String author;
}

@Document标记该实体类为es的实体对象。indexName属性指明在哪个索引下。
@Field标记该变量对应该表的某个字段。其中的属性有:
index:是否对该字段分词,默认true。
analyzer:指定插入时的分词模式,一般为ik_max_words。
type:字段类型。
store:是否存储该字段,默认值为false。
searchAnalyzer:指定查询时的分词模式,一般为ik_smart。

补充说明:
IK中有2种分词模式:ik_max_word和ik_smart。ik_max_words会最细粒度分词,ik_smart会智能分词。具体区别是怎样的这里不赘述。

4.编写测试类

在编写测试类之前,要清楚以下一些知识点:
index:相当于mysql的database
mapping:相当于mysql的table
document:相当于mysql的一条数据,也就是一个java object
field:相当于mysql的一个字段

在了解这些之后,各位应该知道要先做什么了吧。
1).创建数据库,也就是index
2).创建表,也就是mapping
3).增删改查

直接上代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/*.xml")
public class TestClass {

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    
    //方法
    //删除index
    @Test
    public void deleteIndex() {
        boolean b = elasticsearchRestTemplate.deleteIndex("myindex");
        System.out.println(b);
    }
    //创建index
    @Test
    public void createIndex() {
        boolean b = elasticsearchRestTemplate.createIndex("myindex");
        System.out.println(b);
    }

    //创建mapping
    @Test
    public void createMapping() {
        boolean b = elasticsearchRestTemplate.putMapping(Book.class);
        System.out.println(b);
    }

    //查看表字段
    @Test
    public void getMapping() {
        Map<String, Object> mapping = elasticsearchRestTemplate.getMapping(Book.class);
        System.out.println(mapping);
    }

    @Test
    public void saveListToES() {
        ArrayList<Book> books = new ArrayList<>();
        Book book = new Book();
        book.setId("101");
        book.setName("java从入门到入土");
        book.setAuthor("詹姆斯res高级大神");
        Book book1 = new Book();
        book1.setId("102");
        book1.setName("java从入门到入土");
        book1.setAuthor("詹姆斯高斯林");
        Book book2 = new Book();
        book2.setId("103");
        book2.setName("java从入门到入土");
        book2.setAuthor("詹姆斯高斯林");
        Book book3 = new Book();
        book3.setId("104");
        book3.setName("java从入门到入土");
        book3.setAuthor("詹姆斯高斯林");
        books.add(book);
        books.add(book1);
        books.add(book2);
        books.add(book3);
        for (Book book4 : books) {
            IndexQuery indexQuery = new IndexQuery();
            indexQuery.setIndexName("myindex");
            indexQuery.setObject(book4);
            String index = elasticsearchRestTemplate.index(indexQuery);
            System.out.println(index);
        }

    }

    @Test
    public void queryByHighLight() {

//        ElasticsearchTemplate elasticsearchTemplate = new ElasticsearchTemplate();
        String message = "詹姆斯";
        String field = "author";
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.termQuery(field, message))
                .withHighlightFields(new HighlightBuilder.Field(field)).build();
        AggregatedPage<Book> books = elasticsearchRestTemplate.queryForPage(searchQuery, Book.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                ArrayList<Book> books = new ArrayList<>();
                SearchHits hits = response.getHits();
                for (SearchHit searchHit : hits) {
                    if (hits.getHits().length <= 0) {
                        return null;
                    }
                     Book book = new Book();
                    String highLightMessage = searchHit.getHighlightFields().get(field).fragments()[0].toString();
                    book.setId(searchHit.getId());
                    book.setName(String.valueOf(searchHit.getSourceAsMap().get("name")));
                    // 反射调用set方法将高亮内容设置进去
                    try {
                        String setMethodName = parSetName(field);
                        Class<? extends Book> bookClazz = book.getClass();
                        Method setMethod = bookClazz.getMethod(setMethodName, String.class);
                        setMethod.invoke(book, highLightMessage);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    books.add(book);
                }
                if (books.size() > 0) {
                    return new AggregatedPageImpl<T>((List<T>) books);
                }
                return null;
            }

            @Override
            public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
                return null;
            }
        });
        System.out.println(books.getContent());
    }

    private static String parSetName(String fieldName) {
        if (null == fieldName || "".equals(fieldName)) {
            return null;
        }
        int startIndex = 0;
        if (fieldName.charAt(0) == '_')
            startIndex = 1;
        return "set" + fieldName.substring(startIndex, startIndex + 1).toUpperCase()
                + fieldName.substring(startIndex + 1);
    }

}

由于要整合junit和springtest,所以junit版本必须为4.12及以上,我用的4.12的junit和5.0.7的spring-test。

输出结果如下:

[Book{id='103', name='null', author='詹姆斯高斯林'}, Book{id='104', name='null', author='詹姆斯高斯林'}, Book{id='102', name='null', author='詹姆斯高斯林'}, Book{id='101', name='null', author='詹姆斯res高级大神'}]

看到标签em,这个标签叫做强调标签。一般会搭配a标签使用。各位可以自行查看百度搜索的网页代码,高亮词汇前后均有em标签,外层有a标签。

至此,restTemplate操作es高亮查询完美结束!

你可能感兴趣的:(java)