侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

行动起来,活在当下

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

SpringBoot(32)之整合ElasticSearch

zze
zze
2018-08-19 / 0 评论 / 0 点赞 / 1051 阅读 / 21354 字

新建测试 bean:

// zze.springboot.elasticsearch.bean.Product
import io.searchbox.annotations.JestId;

public class Product {

    private Integer id;

    private String name;

    private String remark;

    private Double price;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

SpringBoot 默认支持两种以下两种方式操作 Elasticsearch。

Jest 操作

1、使用 Maven 新建 SpringBoot 项目,引入 Web 场景启动器,导入 Jest 的依赖:

<dependency>
    <groupId>io.searchbox</groupId>
    <artifactId>jest</artifactId>
    <version>5.3.4</version>
</dependency>

2、配置 Elasticsearch 服务主机地址,使用 9200 端口:

# application.properties
spring.elasticsearch.jest.uris=http://192.168.202.136:9200

3、修改文档 bean,使用注解标识主键:

// zze.springboot.elasticsearch.bean.Product
import io.searchbox.annotations.JestId;

public class Product {
    @JestId
    private Integer id;

    private String name;

    private String remark;

    private Double price;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

4、测试:

import io.searchbox.client.JestClient;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import zze.springboot.elasticsearch.bean.Product;

import java.io.IOException;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class JestTests {

    @Autowired
    private JestClient jestClient;

    // 索引文档
    @Test
    public void testIndex() {
        // 创建一个产品作为文档
        Product product = new Product();
        product.setId(1);
        product.setName("iphone 8 plus");
        product.setPrice(5300D);
        product.setRemark("刺激战场首选");
        // 构建一个 product 索引,索引文档到该索引下 phone 类型
        Index index = new Index.Builder(product).index("product").type("phone").build();
        try {
            jestClient.execute(index);
        } catch (IOException e) {
            e.printStackTrace();
        }
                 

    }

    // 搜索
    @Test
    public void testSearch() {
        // 查询表达式
        String json = "{\n" +
                "    \"query\": {\n" +
                "        \"match\": {\n" +
                "            \"name\": \"iphone 8 plus\"\n" +
                "        }\n" +
                "    }\n" +
                "}"; // 空字符串为查询所有
        // 构建搜索对象,指定在 product 索引的 phone 类型下通过 json 变量指定的查询表达式搜索
        Search search = new Search.Builder(json).addIndex("product").addType("phone").build();
        try {
            SearchResult searchResult = jestClient.execute(search);
            List<SearchResult.Hit<Product, Void>> hits = searchResult.getHits(Product.class);
            for (SearchResult.Hit<Product, Void> hit : hits) {
                System.out.println(hit.source);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        /*
        Product{id=1, name='iphone 8 plus'}
         */
    }
}

SpringData操作

SpringBoot 默认使用 SpringData 来操作 Elasticsearch。

Spring Data Elasticsearch 官方文档 | Spring Data Elasticsearch GitHub

1、使用 maven 新建 SpringBoot 项目,引入 Web、Elasticsearch 场景启动器。

2、配置 Elasticsearch 服务主机地址,使用 9300 端口:

# application.properties
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=192.168.202.136:9300

3、修改文档 bean,使用注解指定文档存放的索引及类型:

// zze.springboot.elasticsearch.bean.Product
import org.springframework.data.elasticsearch.annotations.Document;

@Document(indexName = "product",type = "phone") // 指定该类型实例是一个文档对象,存放在 product 索引下 phone 类型中
public class Product {

    private Integer id;

    private String name;

    private String remark;

    private Double price;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

ElasticsearchRepository 操作

1、新建 Repository 接口:

// zze.springboot.elasticsearch.repository.ProductRepository
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import zze.springboot.elasticsearch.bean.Product;

import java.util.List;

public interface ProductRepository extends ElasticsearchRepository<Product, Integer> {

    // 扩展 ElasticsearchRepository 自定义方法,使用可参考官方文档及 GitHub 文档
    public List<Product> findProductByNameLike(String name);
}

2、测试:

import io.searchbox.client.JestClient;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import zze.springboot.elasticsearch.bean.Product;
import zze.springboot.elasticsearch.repository.ProductRepository;

import java.io.IOException;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchRepositoryTests {

    @Autowired
    private ProductRepository productRepository;

    // 索引文档
    @Test
    public void testIndex() {
        // 创建一个产品作为文档
        Product product = new Product();
        product.setId(1);
        product.setName("iphone 8 plus");
        product.setPrice(5300D);
        product.setRemark("刺激战场首选");
        productRepository.index(product);
    }

    // 搜索
    @Test
    public void testSearch() {
        Iterable<Product> products = productRepository.findAll();
        products.forEach(p-> System.out.println(p));
        /*
        Product{id=1, name='iphone 8 plus'}
         */
    }
    // 根据名称查询
    @Test
    public void testFindByName(){
        // like 模糊查询时值不能直接使用空格,需要使用 \b 转义
        List<Product> products = productRepository.findProductByNameLike("iphone\b8");
        products.fotestrEach(p-> System.out.println(p));
        /*
        Product{id=1, name='iphone 8 plus'}
         */
    }
}

关于在接口中扩展查询方法可参考如下范例:

关键字例子对应查询表达式

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(Collection<String>names)

{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}

NotIn

findByNameNotIn(Collection<String>names)

{"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}}}}

不仅如此,还可以通过注解让指定查询表达式绑定到扩展的查询方法,如:

@Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \" ? 0\"}}}}")
Page<Product> findByName(String name, Pageable pageable);

更多使用细节参考官方文档 2.2 节

ElasticsearchTemplate 操作

import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.test.context.junit4.SpringRunner;
import zze.springboot.elasticsearch.bean.Product;
import zze.springboot.elasticsearch.repository.ProductRepository;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchTemplateTests {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    // 索引文档
    @Test
    public void testIndex() {
        // 创建一个产品作为文档
        Product product = new Product();
        product.setId(3);
        product.setName("iphone 9 plus");
        product.setPrice(5300D);
        product.setRemark("刺激战场首选");
        IndexQuery indexQuery = new IndexQueryBuilder().withIndexName("product")
                .withType("phone").withId(product.getId().toString()).withObject(product).build();
        elasticsearchTemplate.index(indexQuery);
    }

    // 搜索
    @Test
    public void testSearch() {
        // 构建查询构建器
        BoolQueryBuilder bqb = QueryBuilders.boolQuery();
        bqb.must(QueryBuilders.boolQuery()
                        .should(QueryBuilders.matchQuery("id","3")));
        // 构建一个搜索查询
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(bqb).withIndices("product").withTypes("phone")
                .withSearchType(SearchType.DEFAULT)
                .build();
        List<Product> products = elasticsearchTemplate.queryForList(searchQuery, Product.class);
        for (Product product : products) {
            System.out.println(product);
        }
        /*
        Product{id=3, name='iphone 9 plus'}
         */
    }
}

注意:SpringData 依赖的 Elasticsearch 依赖版本需要与 Elasticsearch 服务器版本匹配,在 GitHub 中有说明规则:

spring data elasticsearchelasticsearch
3.2.x6.5.0
3.1.x6.2.2
3.0.x5.5.0
2.1.x2.4.0
2.0.x2.2.0
1.3.x1.5.2

如果版本不匹配,会抛出如下异常:

org.elasticsearch.transport.ConnectTransportException: [][192.168.202.136:9300/192.168.202.136:9300

解决方案是修改依赖版本或者重新安装 Elasticsearch 指定版本服务。

0

评论区