高级客户端

参考:尚硅谷网课以及笔记
Java REST Client 有两种风格

在 Elasticsearch 7.0 中不建议使用TransportClient,并且在8.0中会完全删除TransportClient。因此,官方建议我们用Java High Level REST Client。

Java 高级客户端官方文档位置http://www.elastic.co/guide/en/elasticsearch/client/javarest/current/javaresthigh.html

搭建环境

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId> 
        </dependency>


        <!--引入es坐标-->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.8.0</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>7.8.0</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.8.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.4</version>
        </dependency>
    </dependencies>
package com.atguigu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ElasticsearchDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ElasticsearchDemoApplication.class, args);
    }
}
package com.atguigu.config;

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

@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {
    private String host;
    private int port;

    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    @Bean
    public RestHighLevelClient client(){
        return new RestHighLevelClient(RestClient.builder(new HttpHost(host,port,"http")));
    }
}
package com.atguigu.test;

import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.*;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.After;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest
public class ElasticsearchTest {
    @Autowired
    private RestHighLevelClient client;
    @Test
    public void contextLoads() {
        System.out.println(client);
}
}

索引操作

@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchTest {
    @Autowired
    private RestHighLevelClient client;
    /** 添加索引  */
    @Test
    public void addIndex() throws Exception {
        //1.使用client获取操作索引对象
        IndicesClient indicesClient = client.indices();
        //2.具体操作,获取返回值
        CreateIndexRequest createRequest = new CreateIndexRequest("abc");
        CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
        //3.根据返回值判断结果
        System.out.println(response.isAcknowledged());
    }
}

kibana

PUT aaa/_mapping
{
  "properties":{
    "address":{
      "type":"text",
      "analyzer":"ik_max_word"
    },
    "age":{
      "type":"long"
    },
    "name":{
      "type":"keyword"
    }
  }
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchTest {
    @Autowired
    private RestHighLevelClient client;
    /**添加索引映射 */
    @Test
    public void addIndexAndMapping() throws IOException {
        //1.使用client获取操作索引对象
        IndicesClient indicesClient = client.indices();
        //2.具体操作,获取返回值
        CreateIndexRequest createRequest = new CreateIndexRequest("aaa");
        //2.1 设置mappings
        String mapping = "{n" +
                "      "properties" : {n" +
                "        "address" : {n" +
                "          "type" : "text",n" +
                "          "analyzer" : "ik_max_word"n" +
                "        },n" +
                "        "age" : {n" +
                "          "type" : "long"n" +
                "        },n" +
                "        "name" : {n" +
                "          "type" : "keyword"n" +
                "        }n" +
                "      }n" +
                "    }";
        createRequest.mapping(mapping,XContentType.JSON);
        CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
        //3.根据返回值判断结果
        System.out.println(response.isAcknowledged());
    }
}
/**
     * 判断索引是否存在
     */
    @Test
    public void existIndex() throws IOException {
        IndicesClient indices = client.indices();
        GetIndexRequest getRequest = new GetIndexRequest("aaa");
        boolean exists = indices.exists(getRequest, RequestOptions.DEFAULT);
        System.out.println(exists);
    }
/**
     * 查询索引
     */
    @Test
    public void queryIndex() throws IOException {
        IndicesClient indices = client.indices();
        GetIndexRequest getReqeust = new GetIndexRequest("aaa");
        GetIndexResponse response = indices.get(getReqeust, RequestOptions.DEFAULT);
        //获取结果
        Map<String, MappingMetaData> mappings = response.getMappings();
        for (String key : mappings.keySet()) {
           System.out.println(key+":" + mappings.get(key).getSourceAsMap());
        }
    }
 /**
     * 删除索引
     */
    @Test
    public void deleteIndex() throws IOException {
        IndicesClient indices = client.indices();
        DeleteIndexRequest deleteRequest = new DeleteIndexRequest("abc");
        AcknowledgedResponse response = indices.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println(response.isAcknowledged());
    }

文档操作

    /**
     * 添加文档,使用map作为数据
     */
    @Test
    public void addDoc() throws IOException {
        //数据对象map
        Map data = new HashMap();
        data.put("address","hf");
        data.put("name","ad");
        data.put("age",18);

        //1.获取操作文档对象
        IndexRequest request = new IndexRequest("aaa").id("1").source(data);
        //添加数据,获取结果
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);

        //打印响应结果
        System.out.println(response.getId());
    }

创建对象

public class Person {
    private String id;
    private String name;
    private int age;
    private String address;
    // 设置 set get 和tostring方法
}   
/**
* 添加文档,使用对象作为数据
*/
@Test
public void addDoc2() throws IOException {
    //数据对象,javaObject
    Person p = new Person();
    p.setId("2");
    p.setName("hf");
    p.setAge(20);
    p.setAddress("ss");

    //将对象转为json
    String data = JSON.toJSONString(p);

    //1.获取操作文档的对象
    IndexRequest request = new IndexRequest("aaa").id(p.getId()).source(data,XContentType.JSON);
    //添加数据,获取结果
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);

    //打印响应结果
    System.out.println(response.getId());
}
/**
     * 修改文档:添加文档时,如果id存在修改,id不存在则添加
     */
@Test
public void updateDoc() throws IOException {
    //数据对象,javaObject
    Person p = new Person();
    p.setId("2");
    p.setName("硅谷");
    p.setAge(30);
    p.setAddress("北京昌平区");

    //将对象转为json
    String data = JSON.toJSONString(p);

    //1.获取操作文档的对象
    IndexRequest request = new IndexRequest("aaa").id(p.getId()).source(data,XContentType.JSON);
    //添加数据,获取结果
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);

    //打印响应结果
    System.out.println(response.getId());
}
/**
* 根据id查询文档
*/
@Test
public void findDocById() throws IOException {
    GetRequest getReqeust = new GetRequest("aaa","1");
    //getReqeust.id("1");
    GetResponse response = client.get(getReqeust, RequestOptions.DEFAULT);
    //获取数据对应json
    System.out.println(response.getSourceAsString());
}
/**
* 根据id删除文档
*/
@Test
public void delDoc() throws IOException {
    DeleteRequest deleteRequest = new DeleteRequest("aaa","1");
    DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT);
    System.out.println(response.getId());
    System.out.println(response.getResult());
}

Bulk 批量操作是将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数

这里使用的是person索引,之前创建过,没创建的要创建。

PUT person/_mapping
{
  "properties":{
    "address":{
      "type":"text",
      "analyzer":"ik_max_word"
    },
    "age":{
      "type":"long"
    },
    "name":{
      "type":"keyword"
    }
  }
}

下面java代码对应kibana

GET person/_search

# 批量操作
# 1 删除1记录
# 2 添加8记录
# 3 修改2记录 名称为二号

POST _bulk
{"delete":{"_index":"person","_id":"1"}}
{"create":{"_index":"person","_id":"8"}}
{"name":"8号","age":80,"address":"北京"}
{"update":{"_index":"person","_id":"2"}}
{"doc":{"name":"2号"}}
/**
     * 1. 批量操作 bulk
     */
@Test
public void testBulk() throws IOException {
    //创建bulkrequest对象,整合所有操作
    BulkRequest bulkRequest = new BulkRequest();

    /*
        1. 删除1号记录
        2. 添加6号记录
        3. 修改3号记录 名称为 “三号”
         */
    //添加对应操作
    //1. 删除1号记录
    DeleteRequest deleteRequest = new DeleteRequest("person","1");
    bulkRequest.add(deleteRequest);

    //2. 添加6号记录
    Map map = new HashMap();
    map.put("name","六号");
    IndexRequest indexRequest = new IndexRequest("person").id("6").source(map);
    bulkRequest.add(indexRequest);

    Map map2 = new HashMap();
    map2.put("name","三号");
    //3. 修改3号记录 名称为 “三号”
    UpdateRequest updateReqeust = new UpdateRequest("person","3").doc(map2);
    bulkRequest.add(updateReqeust);

    //执行批量操作
    BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    RestStatus status = response.status();
    System.out.println(status);
}

批量导入MySQL到ES

数据库中Goods表的数据导入到ElasticSearch中,自己的表也可以的。

① 创建Goods表并导入数据

· title:商品标题

· price:商品价格

· createTime:创建时间

· categoryName:分类名称。如:家电手机

· brandName:品牌名称。如:华为小米

· spec: 商品规格。如: spec:{“屏幕尺寸”,“5寸”,“内存大小”,“128G”}

· saleNum:销量

· stock:库存

② 创建索引

PUT goods
{
	"mappings": {
		"properties": {
			"title": {
				"type": "text",
				"analyzer": "ik_smart"
			},
			"price": { 
				"type": "double"
			},
			"createTime": {
				"type": "date"
			},
			"categoryName": {	
				"type": "keyword"
			},
			"brandName": {	
				"type": "keyword"
			},	
			"spec": {		
				"type": "object"
			},
			"saleNum": {	
				"type": "integer"
			},			
			"stock": {	
				"type": "integer"
			}
		}
	}
}

# 查询索引
GET goods
  • 添加文档数据

使用kibana操作

POST goods/_doc/1
{
  "title":"小米手机",
  "price":1000,
  "createTime":"2019-12-01",
  "categoryName":"手机",
  "brandName":"小米",
  "saleNum":3000,
  "stock":10000,
  "spec":{
    "网络制式":"移动4G",
    "屏幕尺寸":"4.5"
  }
}

# 查询文档数据
GET goods/_search

java

<!--mybatis-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
# datasource
spring:
  datasource:
    url: jdbc:mysql:///es?serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

# mybatis
mybatis:
  mapper-locations: classpath:/mapper/*Mapper.xml
  type-aliases-package: com.jiao.domain
public class Goods {
    private int id;
    private String title;
    private double price;
    private int stock;
    private int saleNum;
    private Date createTime;
    private String categoryName;
    private String brandName;
    private Map spec; //将数据库中的json串解析成map进行数封装
   // @JSONField(serialize = false)//在转换JSON时,忽略字段
    private String specStr;//接收数据库信息 "{"机身内存":"16G","网络":"联通3G"}"
    // 生成set  get  和 toString方法
}    
  • 创建 dao
@Mapper
public interface GoodsMapper {
    public List<Goods> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jiao.mapper.GoodsMapper">
    <select id="findAll" resultType="goods">
        select
              `id`,
              `title`,
              `price`,
              `stock`,
              `saleNum`,
              `createTime`,
              `categoryName`,
              `brandName`,
              `spec`  as specStr
         from goods
    </select>
</mapper>
@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchTest2 {

    @Autowired
	private GoodsMapper goodsMapper;

    @Autowired
    private RestHighLevelClient client;

    /**
     * 批量导入
     */
    @Test
    public void importData() throws IOException {
        //1.查询所有数据,mysql
        List<Goods> goodsList = goodsMapper.findAll();

        //System.out.println(goodsList.size());
        //2.bulk导入
        BulkRequest bulkRequest = new BulkRequest();

        //2.1 循环goodsList,创建IndexRequest添加数据
        for (Goods goods : goodsList) {
            //2.2 设置spec规格信息 Map的数据   specStr:{}
            //goods.setSpec(JSON.parseObject(goods.getSpecStr(),Map.class));

            String specStr = goods.getSpecStr();
            //将json格式字符串转为Map集合
            Map map = JSON.parseObject(specStr, Map.class);
            //设置spec map
            goods.setSpec(map);
            //将goods对象转换为json字符
            String data = JSON.toJSONString(goods);//bean --> {}
            IndexRequest indexRequest = new IndexRequest("goods");
            indexRequest.id(goods.getId()+"").source(data, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }

        BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(response.status());
    }
}

查询操作

matchAll查询:查询所有文档

kibana

# 查询
GET goods/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0,
  "size": 100
}
/**
     * 查询所有
     *  1. matchAll
     *  2. 将查询结果封装为Goods对象,装载到List中
     *  3. 分页默认显示10条
     */
@Test
public void testMatchAll() throws IOException {
    //2. 构建查询请求对象,指定查询的索引名称
    SearchRequest searchRequest = new SearchRequest("goods");
    //4. 创建查询条件构建器SearchSourceBuilder
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

    //6. 查询条件
    QueryBuilder query = QueryBuilders.matchAllQuery();//查询所有文档
    //5. 指定查询条件
    sourceBuilder.query(query);

    //3. 添加查询条件构建器 SearchSourceBuilder
    searchRequest.source(sourceBuilder);

    // 8. 添加分页信息
    sourceBuilder.from(0);
    sourceBuilder.size(100);

    //1. 查询,获取查询结果
    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    //7. 获取命中对象 SearchHits
    SearchHits searchHits = searchResponse.getHits();
    //7.1 获取总记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);

    List<Goods> goodsList = new ArrayList<>();
    //7.2 获取Hits数据  数组
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        //获取json字符格式的数据
        String sourceAsString = hit.getSourceAsString();
        //转为java对象
        Goods goods = JSON.parseObject(sourceAsString, Goods.class);
        goodsList.add(goods);
    }

    for (Goods goods : goodsList) {
        System.out.println(goods);
    }
}  
  • term查询

term查询:不会对查询条件进行分词

GET goods

# term 查询
GET goods/_search
{
  "query": {
    "term": {
      "categoryName": {
        "value": "手机"
      }
    }
  }
}
/**
     * termQuery:词条查询
     */
@Test
public void testTermQuery() throws IOException {
    SearchRequest searchRequest = new SearchRequest("goods");

    SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

    QueryBuilder query = QueryBuilders.termQuery("title","华为");//term词条查询
    sourceBulider.query(query);

    searchRequest.source(sourceBulider);

    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    SearchHits searchHits = searchResponse.getHits();
    //获取记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);

    List<Goods> goodsList = new ArrayList<>();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
        //转为java
        Goods goods = JSON.parseObject(sourceAsString, Goods.class);
        goodsList.add(goods);
    }

    for (Goods goods : goodsList) {
        System.out.println(goods);
    }
}  

wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意个字符) 和 * (0个或多个字符

prefix查询:前缀查询

# wildcard 查询。查询条件分词模糊查询 华为,华,**
GET goods/_search
{
  "query": {
    "wildcard": {
      "title": {
        "value": "华*" 
      }
    }
  }
}

# 前缀查询
GET goods/_search
{
  "query": {
    "prefix": {
      "brandName": {
        "value": "三"
      }
    }
  }
}
/**
     * 模糊查询:WildcardQuery
     */
@Test
public void testWildcardQuery() throws IOException {
    SearchRequest searchRequest = new SearchRequest("goods");
    SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
    WildcardQueryBuilder query = QueryBuilders.wildcardQuery("title", "华*");
    sourceBulider.query(query);
    searchRequest.source(sourceBulider);
    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    SearchHits searchHits = searchResponse.getHits();
    //获取记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);
    List<Goods> goodsList = new ArrayList<>();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
    //转为java
    Goods goods = JSON.parseObject(sourceAsString, Goods.class);
    goodsList.add(goods);
    }
    for (Goods goods : goodsList) {
        System.out.println(goods);
    }
}
/**
* 模糊查询:perfixQuery
*/
@Test
public void testPrefixQuery() throws IOException {
    SearchRequest searchRequest = new SearchRequest("goods");
    SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
    PrefixQueryBuilder query = QueryBuilders.prefixQuery("brandName", "三");
    sourceBulider.query(query);
    searchRequest.source(sourceBulider);

    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    SearchHits searchHits = searchResponse.getHits();
    //获取记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);

    List<Goods> goodsList = new ArrayList<>();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
    //转为java
    Goods goods = JSON.parseObject(sourceAsString, Goods.class);
    goodsList.add(goods);
    }

    for (Goods goods : goodsList) {
        System.out.println(goods);
    }
}

range 范围查询:查找指定字段指定范围内包含

# 范围查询 gte 大于等于 lte小于等于
GET goods/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 2000,
        "lte": 3000
      }
    }
  }
}

# 范围查询 gte 大于等于 lte小于等于
GET goods/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 2000,
        "lte": 3000
      }
    }
  },
  "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
}
/**
     * 1. 范围查询:rangeQuery
     * 2. 排序
     */
@Test
public void testRangeQuery() throws IOException {
    SearchRequest searchRequest = new SearchRequest("goods");
    SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
    //范围查询
    RangeQueryBuilder query = QueryBuilders.rangeQuery("price");

    //指定下限 gte大于等于
    query.gte(2000);
    //指定上限 小于等于
    query.lte(3000);

    sourceBulider.query(query);

    //排序
    sourceBulider.sort("price", SortOrder.DESC);
    searchRequest.source(sourceBulider);

    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    SearchHits searchHits = searchResponse.getHits();
    //获取记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);

    List<Goods> goodsList = new ArrayList<>();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
        //转为java
        Goods goods = JSON.parseObject(sourceAsString, Goods.class);
        goodsList.add(goods);
    }
    for (Goods goods : goodsList) {
        System.out.println(goods);
    }
}
  • queryString查询

queryString:

• 会对查询条件进行分词
然后分词后的查询条件和词条进行等值匹配
默认取并集(OR)
可以指定多个查询字段

# queryString
GET goods/_search
{
  "query": {
    "query_string": {
      "fields": ["title","categoryName","brandName"],
      "query": "华为手机"
    }
  }
}
/**
* queryString
*/
@Test
public void testQueryStringQuery() throws IOException {
    SearchRequest searchRequest = new SearchRequest("goods");
    SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

    //queryString
    QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手机")
        .field("title")
        .field("categoryName")
        .field("brandName")
        .defaultOperator(Operator.AND);

    sourceBulider.query(query);
    searchRequest.source(sourceBulider);

    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    SearchHits searchHits = searchResponse.getHits();
    //获取记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);

    List<Goods> goodsList = new ArrayList<>();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
        //转为java
        Goods goods = JSON.parseObject(sourceAsString, Goods.class);
        goodsList.add(goods);
    }

    for (Goods goods : goodsList) {
        System.out.println(goods);
    }
}
  • 布尔查询

boolQuery:对多个查询条件连接连接方式

must(and):条件必须成立

must_not(not):条件必须不成立

• should(or):条件可以成立

filter:条件必须成立,性能must高。不会计算得分

# 计算得分
GET goods/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "brandName": {
              "value": "华为"
            }
          }
        }
      ]
    }
  }
}

# 不计算得分
GET goods/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "brandName": {
              "value": "华为"
            }
          }
        }  
      ]
    }
  }
}

# 计算得分 品牌是三星,标题还得电视
GET goods/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "brandName": {
              "value": "三星"
            }
          }
        }
      ],
      "filter": {
        "term": {
          "title": "电视"
        }
      }
    }
  }
}
/**
     * 布尔查询:boolQuery
     * 1. 查询品牌名称为:华为
     * 2. 查询标题包含:手机
     * 3. 查询价格在:2000-3000
     */
@Test
public void testBoolQuery() throws IOException {
    SearchRequest searchRequest = new SearchRequest("goods");
    SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

    //1.构建boolQuery
    BoolQueryBuilder query = QueryBuilders.boolQuery();

    //2.构建各个查询条件
    //2.1 查询品牌名称为:华为
    QueryBuilder termQuery = QueryBuilders.termQuery("brandName","华为");
    query.must(termQuery);

    //2.2. 查询标题包含:手机
    QueryBuilder matchQuery = QueryBuilders.matchQuery("title","手机");
    query.filter(matchQuery);

    //2.3 查询价格在:2000-3000
    QueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
    ((RangeQueryBuilder) rangeQuery).gte(2000);
    ((RangeQueryBuilder) rangeQuery).lte(3000);
    query.filter(rangeQuery);

    //3.使用boolQuery连接
    sourceBulider.query(query);
    searchRequest.source(sourceBulider);
    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    SearchHits searchHits = searchResponse.getHits();
    //获取记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);

    List<Goods> goodsList = new ArrayList<>();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
    //转为java
    Goods goods = JSON.parseObject(sourceAsString, Goods.class);
    goodsList.add(goods);
    }
    for (Goods goods : goodsList) {
        System.out.println(goods);
    }
}
# 查询最贵的华为手机,max_price命名随便取,取一个有意义的名字
GET goods/_search
{
  "query": {
    "match": {
      "title": "华为手机"
    }
  },
  "aggs": {
    "max_price":{
      "max": {
        "field": "price"
      }
    }
  }
}

# 桶聚合 分组
GET goods/_search
{
  "query": {
    "match": {
      "title": "电视"
    }
  },
  "aggs": {
    "goods_brands": {
      "terms": {
        "field": "brandName",
        "size": 100
      }
    }
  }
}
/**
     * 聚合查询:桶聚合,分组查询
     * 1. 查询title包含手机的数据
     * 2. 查询品牌列表
     */
@Test
public void testAggQuery() throws IOException {
    SearchRequest searchRequest = new SearchRequest("goods");
    SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
    // 1. 查询title包含手机的数据
    MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");

    sourceBulider.query(query);
    // 2. 查询品牌列表
    /* 参数:
            1. 自定义的名称,将来用于获取数据
            2. 分组的字段
         */
    AggregationBuilder agg = AggregationBuilders.terms("goods_brands").field("brandName").size(100);
    sourceBulider.aggregation(agg);

    searchRequest.source(sourceBulider);
    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
    SearchHits searchHits = searchResponse.getHits();
    //获取记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);

    List<Goods> goodsList = new ArrayList<>();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
        //转为java
        Goods goods = JSON.parseObject(sourceAsString, Goods.class);
        goodsList.add(goods);
    }

    for (Goods goods : goodsList) {
        System.out.println(goods);
    }

    // 获取聚合结果
    Aggregations aggregations = searchResponse.getAggregations();
    Map<String, Aggregation> aggregationMap = aggregations.asMap();

    //System.out.println(aggregationMap);
    Terms goods_brands = (Terms) aggregationMap.get("goods_brands");
    List<? extends Terms.Bucket> buckets = goods_brands.getBuckets();

    List brands = new ArrayList();
    for (Terms.Bucket bucket : buckets) {
        Object key = bucket.getKey();
        brands.add(key);
    }

    for (Object brand : brands) {
        System.out.println(brand);
    }
}

高亮三要素:

​ • 高亮字段
​ • 前缀
​ • 后缀

GET goods/_search
{
  "query": {
    "match": {
      "title": "电视"
    }
  },
  "highlight": {
    "fields": {
      "title": {
        "pre_tags": "<font color='red'>",
        "post_tags": "</font>"
      }
    }
  }
}
/**
     *
     * 高亮查询:
     *  1. 设置高亮
     *      * 高亮字段
     *      * 前缀
     *      * 后缀
     *  2. 将高亮了的字段数据,替换原有数据
     */
@Test
public void testHighLightQuery() throws IOException {
    SearchRequest searchRequest = new SearchRequest("goods");
    SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

    // 1. 查询title包含手机的数据
    MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");
    sourceBulider.query(query);

    //设置高亮
    HighlightBuilder highlighter = new HighlightBuilder();
    //设置三要素
    highlighter.field("title");
    highlighter.preTags("<font color='red'>");
    highlighter.postTags("</font>");
    sourceBulider.highlighter(highlighter);

    // 2. 查询品牌列表
    /*
        参数:
            1. 自定义的名称,将来用于获取数据
            2. 分组的字段
         */
    AggregationBuilder agg = AggregationBuilders.terms("goods_brands").field("brandName").size(100);
    sourceBulider.aggregation(agg);

    searchRequest.source(sourceBulider);
    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    SearchHits searchHits = searchResponse.getHits();
    //获取记录数
    long value = searchHits.getTotalHits().value;
    System.out.println("总记录数:"+value);

    List<Goods> goodsList = new ArrayList<>();
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
        //转为java
        Goods goods = JSON.parseObject(sourceAsString, Goods.class);
        // 获取高亮结果,替换goods中的title
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        HighlightField HighlightField = highlightFields.get("title");
        Text[] fragments = HighlightField.fragments();
        //替换
        goods.setTitle(fragments[0].toString());
        goodsList.add(goods);
    }

    for (Goods goods : goodsList) {
        System.out.println(goods);
    }
}

Spring Data Elasticsearch

· Spring Data的作用:简化数据库增删改查操作

Spring Data Jpa介绍

JPA是一个规范,真正操作数据库的是Hibernate(实现数据库增删改查框架[ORM框架],操作数据库采用方式面向对象[不写SQL语句]),而spring data jpa是对jpa封装,将CRUD的方法封装指定的方法中,操作的时候,只需要调用方法即可

Spring Data Jpa的实现过程

1:定义实体实体类添加Jpa的注解 @Entity @Table @Cloumn @Id

2:定义接口,接口要继承JpaRepository的接口

3:配置spring容器applicationContext.xml/SpringApplication.run(T.class,args)

Spring Data ElasticSearch简介

(1)Spring Data介绍

(2)Spring Data Elasticsearch介绍

Spring Data Elasticsearch入门

搭建工程

(1)搭建工程

​ 创建项目 elasticsearch-springdata-es

(2)pom.xml依赖

<?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.jiao</groupId>
    <artifactId>elasticsearch-springdata-es</artifactId>
    <version>1.0-SNAPSHOT</version>

     <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

(3) 编写实体类

创建com.jiao.domain.Item,代码如下

@Document(indexName = "item",shards = 1, replicas = 1)
public class Item {
    @Id
    private Long id;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String title; //标题

    @Field(type = FieldType.Keyword)
    private String category;// 分类

    @Field(type = FieldType.Keyword)
    private String brand; // 品牌

    @Field(type = FieldType.Double)
    private Double price; // 价格

    @Field(index = false, type = FieldType.Keyword)
    private String images; // 图片地址

    public Item() {
    }

    public Item(Long id, String title, String category, String brand, Double price, String images) {
        this.id = id;
        this.title = title;
        this.category = category;
        this.brand = brand;
        this.price = price;
        this.images = images;
    }

    //get/set/toString…
}

映射

Spring Data通过注解声明字段的映射属性,有下面的三个注解

@Document 作用在类,标记实体类为文档对象,一般有四个属性

indexName:对应索引库名称
shards:分片数量
replicas:副本数量

@Id 作用在成员变量标记一个字段作为id主键

@Field 作用在成员变量标记为文档的字段,并指定字段映射属性

type:字段类型取值枚举:FieldType
index:是否索引,布尔类型默认true
store:是否存储,布尔类型默认false
analyzer:分词器名称:ik_max_word

(4)配置 application.properties 文件

# es服务地址
elasticsearch.host=127.0.0.1
# es服务端口
elasticsearch.port=9200
# 配置日志级别,开启debug日志
logging.level.com.jiao=debug

(5)配置类

https://docs.spring.io/spring-data/elasticsearch/docs/3.2.3.RELEASE/reference/html/#elasticsearch.mapping.meta-model

https://docs.spring.io/spring-data/elasticsearch/docs/3.2.3.RELEASE/reference/html/#elasticsearch.operations.resttemplate

  • ElasticsearchRestTemplate是spring-data-elasticsearch项目中的一个类,和其他spring项目中的template类似。

  • 在新版的spring-data-elasticsearch中,ElasticsearhRestTemplate代替了原来的ElasticsearchTemplate。原因是ElasticsearchTemplate基于TransportClient,TransportClient即将在8.x以后的版本移除。所以,我们推荐使用ElasticsearchRestTemplate。

  • ElasticsearchRestTemplate基于RestHighLevelClient客户端的。需要自定义配置类,继承AbstractElasticsearchConfiguration,并实现elasticsearchClient()抽象方法,创建RestHighLevelClient对象。

@ConfigurationProperties(prefix = "elasticsearch")
@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
    private String host ;
    private Integer port ;
 
    //重写父类方法
    @Override
    public RestHighLevelClient elasticsearchClient() {
        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
        RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
        return restHighLevelClient; 
    }
}

测试

  • 索引操作
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestSpringBootES {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchTemplate;

    @Test
    public void testCreate() {
        // 创建索引,会根据Item类的@Document注解信息来创建
        elasticsearchTemplate.createIndex(Item.class); // Item是上面我们自己创建的一个实体类
        // 配置映射,会根据Item类中的id、Field等字段来自动完成映射
        elasticsearchTemplate.putMapping(Item.class);
    }
}
  • 增删改操作

Spring Data 的强大之处,就在于你不用写任何DAO处理自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能

public interface ItemRepository extends ElasticsearchRepository<Item,Long>{
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestSpringBootES {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchTemplate;

    @Autowired
    private ItemRepository itemRepository;

    
    //增加
    @Test
    public void testAdd() {
        Item item = new Item(1L, "小米手机7", " 手机", "小米", 3499.00, "http://image.leyou.com/13123.jpg");
        itemRepository.save(item);
    }
    
    //修改(id存在就是修改,否则就是插入)
    @Test
	public void testUpdate() {
    	Item item = new Item(1L, "小米手机7777", " 手机", "小米", 9499.00, "http://image.leyou.com/13123.jpg");
  	  	itemRepository.save(item);
	}
    
    
    //批量新增
    @Test
    public void indexList() {
        List<Item> list = new ArrayList<>();
        list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.leyou.com/123.jpg"));
        list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.leyou.com/3.jpg"));
        // 接收对象集合,实现批量新增
        itemRepository.saveAll(list);
    }
    
    //删除操作
    @Test
    public void testDelete() {
        itemRepository.deleteById(1L);
    }
    
	//根据id查询
    @Test
    public void testQuery(){
        Optional<Item> optional = itemRepository.findById(2L);
        System.out.println(optional.get());
    }
    
	//查询全部,并按照价格降序排序
    @Test
    public void testFind(){
        // 查询全部,并按照价格降序排序
        Iterable<Item> items = this.itemRepository.findAll(Sort.by(Sort.Direction.DESC, "price"));
        items.forEach(item-> System.out.println(item));
    }
    
    
}

自定义方法

Spring Data 的另一个强大功能,是根据方法名称自动实现功能

比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。

当然,方法名称要符合一定的约定:

Keyword Sample Elasticsearch Query String
And findByNameAndPrice {“bool” : {“must” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
Or findByNameOrPrice {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
Is findByName {“bool” : {“must” : {“field” : {“name” : “?”}}}}
Not findByNameNot {“bool” : {“must_not” : {“field” : {“name” : “?”}}}}
Between findByPriceBetween {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
LessThanEqual findByPriceLessThan {“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
GreaterThanEqual findByPriceGreaterThan {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}}
Before findByPriceBefore {“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
After findByPriceAfter {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}}
Like findByNameLike {“bool” : {“must” : {“field” : {“name” : {“query” : “?*”,“analyze_wildcard” : true}}}}}
StartingWith findByNameStartingWith {“bool” : {“must” : {“field” : {“name” : {“query” : “?*”,“analyze_wildcard” : true}}}}}
EndingWith findByNameEndingWith {“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,“analyze_wildcard” : true}}}}}
Contains/Containing findByNameContaining {“bool” : {“must” : {“field” : {“name” : {“query” : “?”,“analyze_wildcard” : true}}}}}
In findByNameIn(Collectionnames) {“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}}
NotIn findByNameNotIn(Collectionnames) {“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}}
Near findByStoreNear Not Supported Yet !
True findByAvailableTrue {“bool” : {“must” : {“field” : {“available” : true}}}}
False findByAvailableFalse {“bool” : {“must” : {“field” : {“available” : false}}}}
OrderBy findByAvailableTrueOrderByNameDesc {“sort” : [{ “name” : {“order” : “desc”} }],“bool” : {“must” : {“field” : {“available” : true}}}}

例如我们来按照价格区间查询,定义这样的一个方法:

public interface ItemRepository extends ElasticsearchRepository<Item,Long>{
    List<Item> findByPriceBetween(double price1, double price2);
}

然后加一些测试数据:


@Test
public void indexList2() {
    List<Item> list = new ArrayList<>();
    list.add(new Item(1L, "小米手机7", "手机", "小米", 3299.00, "http://image.leyou.com/13123.jpg"));
    list.add(new Item(2L, "坚果手机R1", "手机", "锤子", 3699.00, "http://image.leyou.com/13123.jpg"));
    list.add(new Item(3L, "华为META10", "手机", "华为", 4499.00, "http://image.leyou.com/13123.jpg"));
    list.add(new Item(4L, "小米Mix2S", "手机", "小米", 4299.00, "http://image.leyou.com/13123.jpg"));
    list.add(new Item(5L, "荣耀V10", "手机", "华为", 2799.00, "http://image.leyou.com/13123.jpg"));
    // 接收对象集合,实现批量新增
    itemRepository.saveAll(list);
}

需要写实现类,然后我们直接去运行

@Test
public void queryByPriceBetween(){
    List<Item> list = this.itemRepository.findByPriceBetween(2000.00, 3500.00);
    for (Item item : list) {
        System.out.println("item = " + item);
    }
}

虽然基本查询和自定义方法已经很强大了,但是如果是复杂查询(模糊、通配符词条查询等)就显得力不从心了。此时,只能使用原生查询。

原文地址:https://blog.csdn.net/qq_44802369/article/details/134711377

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_25634.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注