商城:整合ElasticSearch

创建es检索服务模块

1.新建module  gulimall-search

2.pom导入依赖

        <!--引入es的JAVA REST Client 高阶依赖-->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.4.2</version>
        </dependency>

这里注意springboot指定的elasticsearch版本肯本可能不一样,需要自己指定一下!

 

    <properties>
        <elasticsearch.version>7.4.2</elasticsearch.version>
    </properties>

3.新建配置类 ElasticsearchConfig

目前暂时用不到RequestOptions,所以在配置类中将一些配置先注释掉,等需要用到再进行配置。

ElasticsearchConfig

 

/**
 * 1. 导入依赖
 * 2. 编写配置,给容器中注入一个RestHighLevelClient
 * 3. 参照API文档操作
 */
@Configuration
public class GulimallElasticSearchConfig {
    /**
     * 配置请求选项
     * 参考:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-low-usage-requests.html#java-rest-low-usage-request-options
     */
    public static final RequestOptions COMMON_OPTIONS;

    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
//        builder.addHeader("Authorization", "Bearer " + TOKEN);
//        builder.setHttpAsyncResponseConsumerFactory(
//                new HttpAsyncResponseConsumerFactory
//                        .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
        COMMON_OPTIONS = builder.build();
    }
    
    @Bean
    public RestHighLevelClient esRestClient() {
        // 集群模式下,需要指明每个es的ip跟端口号,有几个集群就new 几个HttpHost
//        RestHighLevelClient client = new RestHighLevelClient(
//                RestClient.builder(new HttpHost("192.168.190.135", 9200, "http"))
//        );
        // 把上面的拆分一下
        RestClientBuilder builder = null;
        builder = RestClient.builder(new HttpHost("192.168.190.135", 9200, "http"));
        RestHighLevelClient client = new RestHighLevelClient(builder);
        return client;
    }
}

如果无法启动,在主启动类上排除数据库配置类:@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

测试

测试均在GulimallSearchApplicationTests类中完成

测试配置类注入

//@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallSearchApplicationTests {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Test
    public void contextLoads() {
        System.out.println(restHighLevelClient);
    }
}

打印: org.elasticsearch.client.RestHighLevelClient@3686389

测试Index API (存储数据)

参考:Index Api Document

提供文档源(要存储的数据)有很多方式:

  • String字符串
  • Map
  • XContentBuilder 对象
  • KV 键值对
  • 自定义类型对象转JSON字符串 (常用)

文档源设置完成后,便需要执行提交到es,执行分为同步与异步执行,具体可以参考文档。

json测试插入

   @Test
    public void indexData() throws IOException {
        // 创建IndexRequest,并指定index名(在哪个索引下面存储数据)
        IndexRequest indexRequest = new IndexRequest("users");
        indexRequest.id("1"); // 设置id,必须为字符串的形式,不设置会自动生成

        // source方法用于设置文档数据,数据可以有多种格式
        // 1.json字符串  2.Map  3.XContentBuilder  4.KV键值对

        // kv键值对方式传输
        //indexRequest.source("username", "zhangsan", "age", 18, "gender", "男");

        // 将自定义对象转为json字符串传输
        User user = new User();
        user.setUserName("zhangsan");
        user.setGender("男");
        user.setAge(18);
        // 把对象转为json字符串
        String jsonString = JSON.toJSONString(user);// 需要导入com.alibaba.fastjson依赖
        indexRequest.source(jsonString, XContentType.JSON);  // 这里需要指明内容类型

        // 执行保存操作,执行分为同步与异步的方式,这里使用同步执行
        // 只要是网络操作,必然会考虑到异常,index方法会抛出IOException
        IndexResponse indexResponse = client.index(indexRequest, ElasticsearchConfig.COMMON_OPTIONS);


        // 提取有用的响应数据
        System.out.println(indexResponse);
    }

    @Data
    class User {
        private String userName;
        private String gender;
        private Integer age;
    }

执行之前 GET users/_search 查询无数据,执行之后查询有数据!

复杂检索测试

参考:Search API Document

复杂检索测试代码

@Test
public void searchData() throws IOException {
    // 1.创建检索请求
    SearchRequest searchRequest = new SearchRequest();
    // SearchRequest searchRequest = new SearchRequest("bank");  // 也可以创建的时候直接指定index
    searchRequest.indices("bank");  // 指定从哪个index检索
    // 指定DSL,检索条件
    // searchSourceBuilder 封装了全部检索条件
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    // 1.1 构造检索条件
//        searchSourceBuilder.query();
//        searchSourceBuilder.from();
//        searchSourceBuilder.size();
//        searchSourceBuilder.aggregation();
    // 搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄
    searchSourceBuilder.query(QueryBuilders.matchQuery("address", "mill"));
    // 1.2 按照年龄的值分布进行聚集
    TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);
    searchSourceBuilder.aggregation(ageAgg);
    // 1.3 计算match条件得到结果的平均薪资
    AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");
    searchSourceBuilder.aggregation(balanceAvg);

    System.out.println("检索条件" + searchSourceBuilder.toString());
    searchRequest.source(searchSourceBuilder);

    // 2.执行检索  同样分为异步与同步
    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

    // 3.分析检索结果 searchResponse
    System.out.println(searchResponse.toString());
//        JSON.parseObject(searchResponse.toString(), Map.class);
    // 3.1 获取所有查到的文档
    SearchHits hits = searchResponse.getHits();  // 这是外面最大的hits,里面有个hits数组存储的命中文档
    SearchHit[] searchHits = hits.getHits();
    for (SearchHit searchHit : searchHits) {
        /**
         * "_index" : "bank",
         * "_type" : "account",
         * "_id" : "970",
         * "_score" : 5.4032025,
         * "_source"
         */
//            searchHit.getIndex();
        String string = searchHit.getSourceAsString();  // 结果映射成JSON字符串
        Account account = JSON.parseObject(string, Account.class);   // 通过JSON.parseObject 把JSON字符串转为对象,Account如果是内部类必须是static的
        System.out.println("account:" + account);
    }
    // 3.2 获取这次检索到的分析信息
    Aggregations aggregations = searchResponse.getAggregations();  // 获取所有聚合结果
//        for (Aggregation aggregation : aggregations.asList()) {
//            System.out.println("当前聚合名称" + aggregation.getName());
//        }
    Terms ageAgg1 = aggregations.get("ageAgg");
    for (Terms.Bucket bucket : ageAgg1.getBuckets()) {
        System.out.println("年龄:" + bucket.getKeyAsString() + "==>" + bucket.getDocCount());
    }
    Avg balanceAvg1 = aggregations.get("balanceAvg");
    System.out.println(balanceAvg1.getValue());
}

@Data
@ToString
static class Account {

    private int account_number;
    private int balance;
    private String firstname;
    private String lastname;
    private int age;
    private String gender;
    private String address;
    private String employer;
    private String email;
    private String city;
    private String state;
}

 

阅读剩余
THE END