Java 接入 ElasticSearch

Java 接入 ElasticSearch 的三种方式

Node 接入

将 Java 应用作为一个 Node 节点,加入到 ElasticSearch 的集群中,这种方式太重了,不适合 Java 应用。

Transport 接入

基于 Transport 协议和 9300 端口,进行二进制协议层的交互,效率较好,5.0 开始就不用了。

HTTP 接入

5.0 开始推荐的方式,连接 3 个节点中的任何一点,发送 HTTP 请求就可以。


引入 ElasticSearch 的依赖

  • 注意版本号要和 ElasticSearch 的版本号一致;
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>7.3.0</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.3.0</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.3.0</version>
</dependency>

application.properties 中配置 ElasticSearch 相关信息

#申明es服务地址,只配一个就行了
elasticsearch.ip=127.0.0.1:9200

创建配置 ElasticSearch 的 Bean

package tech.lixinlei.dianping.config;

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

@Configuration
public class ElasticsearchRestClient {

    @Value("${elasticsearch.ip}")
    String ipAddress;

    @Bean(name="highLevelClient")
    public RestHighLevelClient highLevelClient(){
        String[] address = ipAddress.split(":");
        String ip = address[0];
        int port = Integer.valueOf(address[1]);
        HttpHost httpHost = new HttpHost(ip, port, "http");
        return new RestHighLevelClient(RestClient.builder(new HttpHost[]{httpHost}));
    }

}

搜索业务流程

  • H5 发送 keyword 给 Controller;
  • Controller 透传 keyword 到 Service;
  • Service 通过 Rest Client 向 ElasticSearch 发送搜索请求;
  • ElasticSearch 返回 Service 一个 Document ID;
  • Service 拿着 Document ID 到 MySQL 中查询门店信息,完了组成成一个完整的实体,返回给 H5;

Java 接入 ElasticSearch 的大致流程

  • 先在 Kibana 的 Dev Tools 中编写 Query DSL;
  • 再用 JSON 的 API 把 Query DSL 组成起来;
  • 最后调用 ElasticSearch 的 Java API 完成搜索请求的发送;

Java 组装 Query DSL | 举个栗子

  • 其实就是把 搜索模型的建立 中的几个 Query DSL 用 if ... else ... 的逻辑给组装在一起了;
  • 写的时候有个小技巧,在 Dev Tools 中,把 DSL 全折起来,拼一个,打开一个;
@Override
public Map<String, Object> searchES(BigDecimal longitude,
                                    BigDecimal latitude,
                                    String keyword,
                                    Integer orderby,
                                    Integer categoryId,
                                    String tags) throws IOException {

    Request request = new Request("GET","/shop/_search");

    //构建请求
    JSONObject jsonRequestObj = new JSONObject();

    //构建source部分
    jsonRequestObj.put("_source", "*");

    //构建自定义距离字段
    jsonRequestObj.put("script_fields",new JSONObject());
    jsonRequestObj.getJSONObject("script_fields").put("distance",new JSONObject());
    jsonRequestObj.getJSONObject("script_fields").getJSONObject("distance").put("script",new JSONObject());
    jsonRequestObj.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
            .put("source","haversin(lat, lon, doc['location'].lat, doc['location'].lon)");
    jsonRequestObj.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
            .put("lang","expression");
    jsonRequestObj.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
            .put("params",new JSONObject());
    jsonRequestObj.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
            .getJSONObject("params").put("lat",latitude);
    jsonRequestObj.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
            .getJSONObject("params").put("lon",longitude);

    //构建query
    jsonRequestObj.put("query",new JSONObject());

    //构建function score
    jsonRequestObj.getJSONObject("query").put("function_score",new JSONObject());

    //构建function score 内的 query
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").put("query",new JSONObject());
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").put("bool",new JSONObject());
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool").put("must",new JSONArray());
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
            .getJSONArray("must").add(new JSONObject());

    //构建第一个 query 的条件
    int queryIndex = 0;
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
            .getJSONArray("must").getJSONObject(queryIndex).put("match",new JSONObject());
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
            .getJSONArray("must").getJSONObject(queryIndex).getJSONObject("match").put("name", new JSONObject());
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
            .getJSONArray("must").getJSONObject(queryIndex).getJSONObject("match").getJSONObject("name").put("query", keyword);
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
            .getJSONArray("must").getJSONObject(queryIndex).getJSONObject("match").getJSONObject("name").put("boost", 0.1);

    //构建第二个 query 的条件
    queryIndex++;
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
            .getJSONArray("must").add(new JSONObject());
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
            .getJSONArray("must").getJSONObject(queryIndex).put("term", new JSONObject());
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
            .getJSONArray("must").getJSONObject(queryIndex).getJSONObject("term").put("seller_disabled_flag", 0);

    // 页面传来的有 catetory_id 字段
    if(categoryId != null){
        queryIndex++;
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                .getJSONArray("must").add(new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                .getJSONArray("must").getJSONObject(queryIndex).put("term",new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                .getJSONArray("must").getJSONObject(queryIndex).getJSONObject("term").put("category_id", categoryId);
    }

    if(tags != null){
        queryIndex++;
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                .getJSONArray("must").add(new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                .getJSONArray("must").getJSONObject(queryIndex).put("term",new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                .getJSONArray("must").getJSONObject(queryIndex).getJSONObject("term").put("tags",tags);
    }


    //构建functions部分
    jsonRequestObj.getJSONObject("query").getJSONObject("function_score").put("functions",new JSONArray());

    int functionIndex = 0;
    if(orderby == null) {
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").add(new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("gauss", new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss").put("location", new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss")
                .getJSONObject("location").put("origin", latitude.toString() + "," + longitude.toString());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss")
                .getJSONObject("location").put("scale", "100km");
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss")
                .getJSONObject("location").put("offset", "0km");
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss")
                .getJSONObject("location").put("decay", "0.5");
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("weight", 9);

        functionIndex++;
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").add(new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("field_value_factor", new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("field_value_factor")
                .put("field", "remark_score");
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("weight", 0.2);

        functionIndex++;
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").add(new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("field_value_factor", new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("field_value_factor")
                .put("field", "seller_remark_score");
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("weight", 0.1);


        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").put("score_mode","sum");
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").put("boost_mode","sum");
    } else {
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").add(new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("field_value_factor",new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("field_value_factor")
                .put("field", "price_per_man");

        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").put("score_mode", "sum");
        jsonRequestObj.getJSONObject("query").getJSONObject("function_score").put("boost_mode", "replace");
    }

    //排序字段
    jsonRequestObj.put("sort",new JSONArray());
    jsonRequestObj.getJSONArray("sort").add(new JSONObject());
    jsonRequestObj.getJSONArray("sort").getJSONObject(0).put("_score",new JSONObject());
    if(orderby == null){
        jsonRequestObj.getJSONArray("sort").getJSONObject(0).getJSONObject("_score").put("order","desc");
    }else{
        jsonRequestObj.getJSONArray("sort").getJSONObject(0).getJSONObject("_score").put("order","asc");
    }

    //聚合字段
    jsonRequestObj.put("aggs",new JSONObject());
    jsonRequestObj.getJSONObject("aggs").put("group_by_tags",new JSONObject());
    jsonRequestObj.getJSONObject("aggs").getJSONObject("group_by_tags").put("terms", new JSONObject());
    jsonRequestObj.getJSONObject("aggs").getJSONObject("group_by_tags").getJSONObject("terms").put("field", "tags");

    String reqJson = jsonRequestObj.toJSONString();

    System.out.println(reqJson);
    request.setJsonEntity(reqJson);
    Response response = highLevelClient.getLowLevelClient().performRequest(request);
    String responseStr = EntityUtils.toString(response.getEntity());
    System.out.println(responseStr);
    JSONObject jsonObject = JSONObject.parseObject(responseStr);
    JSONArray jsonArr = jsonObject.getJSONObject("hits").getJSONArray("hits");
    List<ShopModel> shopModelList = new ArrayList<>();
    for(int i = 0; i < jsonArr.size(); i++){
        JSONObject jsonObj = jsonArr.getJSONObject(i);
        Integer id = new Integer(jsonObj.get("_id").toString());
        BigDecimal distance = new BigDecimal(jsonObj.getJSONObject("fields").getJSONArray("distance").get(0).toString());
        ShopModel shopModel = get(id);
        shopModel.setDistance(distance.multiply(new BigDecimal(1000).setScale(0,BigDecimal.ROUND_CEILING)).intValue());
        shopModelList.add(shopModel);
    }

    List<Map> tagsList = new ArrayList<>();
    JSONArray tagsJsonArray = jsonObject.getJSONObject("aggregations").getJSONObject("group_by_tags").getJSONArray("buckets");
    for(int i = 0; i < tagsJsonArray.size(); i++){
        JSONObject jsonObj = tagsJsonArray.getJSONObject(i);
        Map<String,Object> tagMap = new HashMap<>();
        tagMap.put("tags", jsonObj.getString("key"));
        tagMap.put("num", jsonObj.getInteger("doc_count"));
        tagsList.add(tagMap);
    }

    Map<String, Object> result = new HashMap<>();
    result.put("tags", tagsList);
    result.put("shop", shopModelList);
    return result;
}
### Java接入 Elasticsearch 的方法 在 Java 应用程序中集成 Elasticsearch 可以通过多种方式实现,以下是两种常见的方法及其具体操作: #### 方法一:基于 Spring Data Elasticsearch 集成 Spring 提供了一个名为 `Spring Data Elasticsearch` 的模块,它简化了与 Elasticsearch 进行交互的过程。此方法适合于需要快速开发 CRUD 功能的应用场景。 1. **引入依赖** 在项目的 Maven 或 Gradle 文件中添加以下依赖项: ```xml <!-- Maven --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> ``` 2. **配置文件设置** 修改 `application.properties` 或 `application.yml` 文件来指定 Elasticsearch 实例的地址。 ```properties spring.elasticsearch.rest.uris=http://localhost:9200 ``` 3. **创建实体类** 定义一个 POJO 类并标注为 `@Document` 来映射到 Elasticsearch 索引。 ```java @Document(indexName = "example_index") public class ExampleEntity { private String id; private String field1; private int field2; // Getters and Setters } ``` 4. **定义 Repository 接口** 创建继承自 `ElasticsearchRepository` 的接口用于执行数据访问逻辑。 ```java public interface ExampleRepository extends ElasticsearchRepository<ExampleEntity, String> { } ``` 5. **使用 Repository 执行 CURD 操作** 调用 Repository 方法完成基本的数据增删改查功能[^1]。 --- #### 方法二:使用官方 REST High Level Client (RHL) 这是由 Elasticsearch 自己提供的高级客户端库,支持更复杂的查询需求以及更好的性能优化能力。 1. **导入所需依赖包** 同样,在构建工具配置文件里加入如下内容: ```xml <!-- Maven --> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.x.x</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.x</version> </dependency> ``` 2. **初始化 RHL 对象实例** 使用下面代码片段建立连接至目标节点的服务端口上运行着的一个 HTTP 主机对象。 ```java RestHighLevelClient client = new RestHighLevelClient( RestClient.builder(new HttpHost("localhost", 9200, "http"))); ``` 3. **编写业务逻辑处理函数** 下面展示了一段完整的例子用来验证当前环境下的可用状态是否正常工作。 ```java try { InfoResponse info = client.info(); System.out.println(info.getClusterName()); } catch (IOException e) { e.printStackTrace(); } finally { try { client.close(); } catch (IOException ex){ ex.printStackTrace(); } } ``` 上述两部分分别介绍了利用框架封装好的 API 和直接调用底层通信协议的方式来进行索引管理及文档检索等活动[^2]。 --- ### 总结 无论是采用轻量级解决方案还是追求灵活性更高的原生驱动方案都可以满足大多数实际项目中的技术选型考量因素。开发者应根据自身团队的技术栈熟悉程度权衡利弊后再做决定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值