es模糊查询

  • 前言
  • 一、es 索引建立以及kibana的相关操作
  • 1.1 kibana操作
  • 1.1.1 新建索引
  • 1.1.2 在kibana上新建索引,看到以下提示就是新建成功
  • 1.1.3 删除索引
  • 1.1.4 查询所有数据
  • 1.1.5 查看索引的映射情况
  • 二、使用步骤
  • 1.本文基于springboot,要先引入maven依赖
  • 2.建立连接
  • 3 es插入数据
  • 3.1注入client
  • 3.2 插入数据
  • 3.2.1 插入数据control层如下
  • 3.2.2 注意点
  • 3.查询
  • 3.1 查询代码如下
  • 三、代码地址
  • 总结



前言

es是一个优秀的搜索框架,最近项目上有用到,但是csdn能找到的资料有限,因此出个文章仅供参考,不对的地方大家可以指出,共同进步。 本文针对初学es使用es新建索引到查询教程,本文基于es版本为:elasticsearch-7.14.1-linux-x86_64.tar.gz, kibana版本为:kibana-7.14.1-linux-x86_64.tar.gz 安装过程就不介绍了,同学们可以自行百度安装。


提示:以下是本篇文章正文内容,下面案例可供参考

一、es 索引建立以及kibana的相关操作

1.1 kibana操作

我们常见的搜索类型有
1.time 可以有很多格式的时间类型,我这里使用了最常用的格式, 2.keyword,使用在已经确定的枚举类型的,例如男、女
3.text,使用在模糊查找的字段,例如文章内容等
4.list,使用在1对多的关系中,例如一个人的标签,可爱、善良
这里我都新建了这几种类型,可以参考如何使用。

1.1.1 新建索引

新建索引名称为test,属性有name,nameType,time 和一个tag标签list

PUT /test
{
  "mappings": {
    "properties": {
        "name":    { "type": "text"  },
        "nameType":     { "type": "text"  },
        "time": {
          "type": "date",
          "format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || yyyy/MM/dd HH:mm:ss|| yyyy/MM/dd ||epoch_millis"
        },
      "tags": {
        "properties": {
          "id": {"type":"keyword"},
                  "tagName": {
                    "type": "text"
                  }
        }
      }
      }
    }

}

1.1.2 在kibana上新建索引,看到以下提示就是新建成功

ireport java 生成分页 java es分页_JSON

1.1.3 删除索引

DELETE /test

ireport java 生成分页 java es分页_java_02

1.1.4 查询所有数据

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

1.1.5 查看索引的映射情况

GET /test

二、使用步骤

1.本文基于springboot,要先引入maven依赖

代码如下(示例):

<properties>
        <java.version>1.8</java.version>
        <elasticsearch.version>7.14.1</elasticsearch.version>
    </properties>
<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency> 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.9</version>
        </dependency>
        <dependency>
            <groupId>apache-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.7.0</version>
        </dependency>

主要是spring-boot-starter-data-elasticsearch,其他有用到就一起粘贴下来了。使用的springboot版本为

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

2.建立连接

建立如下的配置,在control层就可以直接注入使用连接
一般es的端口为9200,如果有账号密码的也可以使用账号密码连接
这里就不展示了,可以看看api就知道了。

代码如下(示例):

package com.example.springdemo.demo.conifg;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class EsConfig {

        @Bean("restHighLevelClient")
        public RestHighLevelClient esRestClient(){
            RestHighLevelClient client = new RestHighLevelClient(
                    RestClient.builder(
                            //在这里配置你的elasticsearch的地址
                            new HttpHost("123.xx.xx.xxx", 9200, "http")
                    )
            );
            return client;
        }

}

3 es插入数据

3.1注入client

@Autowired
    private RestHighLevelClient restHighLevelClient;

3.2 插入数据

这里只介绍了单个插入,批量插入使用client.bulk方法即可

private String insertData(EsModel esModel) throws IOException {
    //要插入的索引名称test
        IndexRequest request = new IndexRequest("test");
        request.id(UUID.randomUUID().toString());
        request.timeout(TimeValue.timeValueSeconds(1));
        request.timeout("1s");
        //将我们的数据放入请求,json
        request.source(JSON.toJSONString(esModel),XContentType.JSON);
        //request.source(describe);
        System.out.println("json:"+JSON.toJSONString(esModel));
        //客服端发送请求
        IndexResponse index = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        request.source(JSON.toJSONString(esModel),XContentType.JSON);
        String result = index.toString()+"/n"+index.toString();
        System.out.println(result);
        return result;
    }

这里的插入格式es支持两种格式,一种是json格式的,如上面的写法,一种是map格式的,

JSON.parseObject(JSON.toJSONString(user), Map.class);

map格式的可以使用json的工具类直接转换即可。

3.2.1 插入数据control层如下

@RequestMapping("/testInsert")
    String testInsert() throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        StringBuilder sb = new StringBuilder();
        EsModel user = new EsModel();
        user.setName("王小明");
        user.setNameType("男");
        user.setTime(new Date());
        List<Tag> tags = new ArrayList<>();
        Tag tag = new Tag();
        tag.setId(UUID.randomUUID().toString());
        tag.setTagName("活泼");
        Tag tag1 = new Tag();
        tag1.setId(UUID.randomUUID().toString());
        tag1.setTagName("开朗");
        Tag tag11= new Tag();
        tag11.setId(UUID.randomUUID().toString());
        tag11.setTagName("活泼2");
        tags.add(tag);
        tags.add(tag1);
        tags.add(tag11);
        user.setTags(tags);
        String s1 = insertData(user);
        sb.append(s1).append("/n");

        EsModel user2 = new EsModel();
        user2.setName("王小二");
        user2.setNameType("男");
        user2.setTime(new Date());
        List<Tag> tags4 = new ArrayList<>();
        Tag tag4 = new Tag();
        tag4.setId(UUID.randomUUID().toString());
        tag4.setTagName("高富活泼");
        Tag tag5 = new Tag();
        tag5.setId(UUID.randomUUID().toString());
        tag5.setTagName("英俊");
        tags4.add(tag4);
        tags4.add(tag5);
        user2.setTags(tags4);
        String s3 = insertData(user2);
        sb.append(s3).append("/n");


        EsModel user1 = new EsModel();
        user1.setName("王小红");
        user1.setNameType("女");
        user1.setTime(new Date());
        List<Tag> tags1 = new ArrayList<>();
        Tag tag2 = new Tag();
        tag2.setId(UUID.randomUUID().toString());
        tag2.setTagName("漂亮");
        Tag tag3 = new Tag();
        tag3.setId(UUID.randomUUID().toString());
        tag3.setTagName("可爱");
        tags1.add(tag3);
        tags1.add(tag2);
        user1.setTags(tags1);
        String s = insertData(user1);
        sb.append(s).append("/n");
        return  sb.toString();

    }

3.2.2 注意点

这里需要注意的是,如果格式是时间格式,我们需要在实体上加个jsonfield注解否则转换成string的时候会报错。实体类如下

package com.example.springdemo.demo.model;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.io.Serializable;
import java.text.Format;
import java.util.Date;
import java.util.List;

@Data
public class EsModel implements Serializable {

    private String name;
    private String nameType;

//    @Field(type = FieldType.Date, format= DateFormat.custom,pattern ="yyyy-MM-dd HH:mm:ss")
//    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private Date time;

    private List<Tag> tags;

}
package com.example.springdemo.demo.model;

import lombok.Data;

@Data
public class Tag {
    private String id;
    private String tagName;
}

执行插入指令显示如下
http://localhost:8008/search/testInsertireport java 生成分页 java es分页_java_03
去kibana查询下数据是否插入
执行上面的查询所有数据指令可以看到数据已经成功插入
ireport java 生成分页 java es分页_spring_04

3.查询

接下来我们可以进行查询了,这里我们主要用的是中文查询,中文搜索的时候,和查询的时候都可以指定分词器,分词器的作用其实就是可以智能的把语句分成不同的词语索引,例如我爱中国,可以分解成,我,爱,中国,如果不加分词器,则会分解成我,爱,中,国,和更多的组合,一般会使用分词器,我这里就不使用了。
这样在搜索的时候就可以更精确的查找到用户想要的结果。
我们可以在kibana测试一下

GET _analyze
{"text":"我爱中国"}

ireport java 生成分页 java es分页_JSON_05


使用分词器分词

GET _analyze
{"analyzer":"ik_max_word",
  "text":"我爱中国"}

ireport java 生成分页 java es分页_ireport java 生成分页_06

3.1 查询代码如下

实际项目使用的时候往往不会像一般简单教程里面的这么简单,分页,模糊查询,高亮,排序,是基本的要求,所以我就构造了下面的查询
这里的查询条件构造如下:
查询一个boolquery,也就是一个是否的条件查询
这里查询为:并且(must) nametype 含有男
并且(代码里面用must表示)
(
或者(代码用shuld) tags.tagName 中含有 活字
)
list里面的字段用list名字.属性表示。

@RequestMapping("/testSearch")
    String contextLoads() throws IOException {
        //1、创建查询请求,规定查询的索引
        SearchRequest request = new SearchRequest("test");
        //2、创建条件构造
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //3、构造条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("nameType","男"));
        BoolQueryBuilder condition = new BoolQueryBuilder();

        condition.should(QueryBuilders.matchPhraseQuery("tags.tagName","活"));

        boolQueryBuilder.must(condition);

        builder.query(boolQueryBuilder);

        //高亮查询
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        HighlightBuilder.Field field = new HighlightBuilder.Field("tags.tagName");
        highlightBuilder.field(field);
        highlightBuilder.requireFieldMatch(false);
//        highlightBuilder.preTags("<em>");
//        highlightBuilder.postTags("</em>");
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");

        builder.highlighter(highlightBuilder);



        //指定返回的字段
        String[] fetch ={"name","nameType","tags.tagName"};
        builder.fetchSource(fetch,null);

        builder.from(0);
        builder.size(10);

        //根据匹配度排序
        builder.sort("_score",SortOrder.DESC);
        //根据时间排序,asc升序
        builder.sort("time", SortOrder.DESC);

        //4、将构造好的条件放入请求中
        request.source(builder);

        //5、开始执行发送request请求
        SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);

        //6、开始处理返回的数据
        SearchHit[] hits = searchResponse.getHits().getHits();
        StringBuilder sb = new StringBuilder();
        for (SearchHit hit : hits) {
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            System.out.println("highlight"+highlightFields);
            HighlightField content = highlightFields.get("tags.tagName");
            //原始数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            System.out.println("sourcemap"+sourceAsMap);
            //高亮的字段替换
            if (content!=null){
                Text[] fragments = content.fragments();
                sourceAsMap.put("tags",Arrays.asList(fragments));
            }
            sb.append(sourceAsMap);
        }
        System.out.println("sb"+ sb.toString());
        return sb.toString();
    }

这里的"<em"高亮显示,但是页面上不显示出来,这里换成了页面可以显示出来的格式"span >"

查询的结果如图所示

ireport java 生成分页 java es分页_java_07

三、代码地址

代码已经上传仓库,感兴趣的可以下载看看
https://gitee.com/jiangbingsong/springboot-demo.git

总结

例如:以上就是今天要讲的内容,本文仅仅简单介绍了es的基本使用,更多的查询例如termsquery精确查找,和fuzzyquery近似查找,例如国中,可以帮你搜索出中国,还有范围查询等等高级使用,可以查询es官方文档,这里仅作为一个入门介绍