elasticsearch在springboot中的搜索应用

1.需求分析

   近期,笔者项目中提出这样的需求:在一定的距离范围内,乘客搜索附近车主的订单列表;这条需求最关键的点在于在一定范围内,并且还得计算附近车主距离乘客当前位置多少公里;笔者思虑仅仅依赖数据库势必会导致搜索性能低下,甚至会加重mysql数据库的请求压力,于是笔者顺理成章的选择了高可用的elasticsearch

2.集成方案

①es的pom依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

②application.yml配置es

spring:
  data:  #elasticsearch
        elasticsearch:
          properties:
            path:
              logs:  /elasticsearch/log  #elasticsearch日志存储目录
              data:  /elasticsearch/data  #./elasticsearch/data #elasticsearch数据存储目录
          cluster-nodes: ip:9300
          cluster-name: elasticsearch

③es应用实体

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName= EsProperties.ES_SERVICE_INDEX,type= EsProperties.ES_SERVICE_TYPE,indexStoreType= EsProperties.ES_SERVICE_STORE_TYPE,shards= EsProperties.ES_SERVICE_SHARES,replicas= EsProperties.ES_SERVICE_REPLICAS,refreshInterval= EsProperties.ES_SERVICE_REFRESH_INTERVAL)
public class EsCarService implements Serializable {

    @Id
    private String serviceId;

    private Integer pinPublishRole;

    /**
     * 当前地理位置经纬度
     * lat纬度,lon经度 "40.715,-74.011"
     * 如果用数组则相反[-73.983, 40.719]
     */
    @GeoPointField
    private String currentAddress;

    private String userId;

    private String pinLeaveWords;

    private String pinStartPoint;

    private String pinEndPoint;

    private BigDecimal pinServiceAccount;

    private BigDecimal pinTotalAccount;

    private String pinServiceCode;

    private Integer passerRequireSeats;

    private Date leaveOffTime;

    private BigDecimal pinTotalDistance;

    private BigDecimal pinLoseAccount;

    private Integer ownerHasSeats;

    private BigDecimal pinStartLatitude;

    private BigDecimal pinStartLongitude;

    private BigDecimal pinEndLatitude;

    private BigDecimal pinEndLongitude;

    private BigDecimal startGapDistance; //距离差
}

④es静态属性配置

public class EsProperties {
    public static final String ES_SERVICE_INDEX = "online";

    public static final String ES_SERVICE_TYPE = "pinCarService";

    public static final String ES_SERVICE_STORE_TYPE = "fs";

    public static final String ES_SERVICE_FILTER_QUERY_NAME = "pinPublishRole";

    public static final String ES_SERVICE_DISTANCE_TO = "startGapDistance";

    public static final Integer ES_SERVICE_SEARCH_CAR_OWNER = 1;

    public static final Integer ES_SERVICE_SEARCH_PASSENGER = 0;

    public static final short ES_SERVICE_SHARES = 5;

    public static final short ES_SERVICE_REPLICAS = 0; //不分配副片

    public static final String ES_SERVICE_REFRESH_INTERVAL = "-1";

    public static final String ES_SERVICE_GEO_ADDRESS_FILED = "currentAddress";

    public static final Double ES_SERVICE_SEARCH_DISTANCE_RANGE = 50.0;
    
}

在配置副分区的时候,如果有集群部署,可以根据需求设置,单机的话选择设置为0就好;

⑤es的api工具类

@Slf4j
public class EsClient {

    private static String host = PropertiesUtil.ELASTIC_SEARCH_HOST;
    private static int port = PropertiesUtil.ELASTIC_SEARCH_PORT;
    private static String clusterName = PropertiesUtil.ELASTIC_SEARCH_CLUSTER_NAME;

    public static Client getClient (){

        Settings settings = Settings.settingsBuilder().put("cluster.name", clusterName).build();// 设置集群名称
        Client client = null;
        try {
            client = TransportClient.builder().settings(settings).build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), port));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return client;

    }

    /**
     * 根据文档名、字段名、字段值查询某一条记录的详细信息
     * @param type  文档名,相当于mysql中的表名
     * @param key 字段名
     * @param value  字段值
     * @return  List
     * @author zx
     */
    public static List getQueryListByField(String index,String type,String key,Object value,SortBuilder sortBuilder,QueryBuilder queryBuilder,Integer pageNo,Integer pageSize){
        Client client = getClient();
        SearchResponse response = client.prepareSearch(index)
                .setTypes(type)
                .setQuery(QueryBuilders.termQuery(key, value))
                .addSort(sortBuilder)
                .setPostFilter(queryBuilder)
                .setFrom(pageNo)
                .setSize(pageSize)
                .setExplain(true)
                .execute()
                .actionGet();
        return response2List(client,response);
    }


    /**
     * 多条件  文档名、字段名、字段值
     * @param type 文档名
     * @param map 字段名
     * @return List
     * @author zx
     */
    public static List getBoolDataByFields(String index,String type,Integer pageNo,Integer pageSize,Map<String,String> map){
        Client client = getClient();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        for (String in : map.keySet()) {
            //map.keySet()返回的是所有key的值
            String str = map.get(in);//得到每个key多对用value的值
            boolQueryBuilder.must(QueryBuilders.termQuery(in,str));
        }
        SearchResponse response = client.prepareSearch(index).setTypes(type)
                .setQuery(boolQueryBuilder)
                .setFrom(pageNo).setSize(pageSize).setExplain(true)
                .execute()
                .actionGet();
        return response2List(client,response);
    }


    /**
     * 单条件 模糊查询
     * @param type 文档名
     * @param key 字段名
     * @param value 字段名模糊值:如 *123* ;?123*;?123?;*123?;
     * @return List
     * @author zx
     */
    public static List getDataByillegible(String index,String type,String key,Integer pageNo,Integer pageSize,String value){
        Client client = getClient();
        WildcardQueryBuilder wBuilder = QueryBuilders.wildcardQuery(key, value);
        SearchResponse response = client.prepareSearch(index).setTypes(type)
                .setQuery(wBuilder)
                .setFrom(pageNo).setSize(pageSize).setExplain(true)
                .execute()
                .actionGet();
        return response2List(client,response);
    }

    /**
     * 多条件 模糊查询
     * @param type  type 文档名
     * @param map   包含key:value 模糊值键值对
     * @return List
     * @author zx
     */
    public static List getDataByMuchillegible(String index,String type,Integer pageNo,Integer pageSize,Map<String,String> map){
        Client client = getClient();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        for (String in : map.keySet()) {
            //map.keySet()返回的是所有key的值
            String str = map.get(in);//得到每个key多对用value的值
            boolQueryBuilder.must(QueryBuilders.wildcardQuery(in,str));
        }
        SearchResponse response = client.prepareSearch(index).setTypes(type)
                .setQuery(boolQueryBuilder)
                .setFrom(pageNo).setSize(pageSize).setExplain(true)
                .execute()
                .actionGet();

        return response2List(client,response);
    }


    /**
     * 将查询后获得的response转成list
     * @param client
     * @param response
     * @return zx
     */
    public static List response2List(Client client,SearchResponse response){
        SearchHits hits = response.getHits();
        List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
        for (int i = 0; i < hits.getHits().length; i++) {
            BigDecimal geoDis = new BigDecimal((Double) hits.getAt(i).getSortValues()[0]);
            Map<String, Object> map = hits.getAt(i).getSource();
            map.put(EsProperties.ES_SERVICE_DISTANCE_TO,geoDis);
            list.add(map);
        }
        client.close();
        return list;
    }

}

⑥es接口类申明

/**
 * @auther zx
 * @date 2018/6/2 17:14
 */

public interface EsPinCarServiceRespority extends ElasticsearchRepository<EsCarService, String> {


}

⑦工程需求应用

车主发布订单时,存储订单信息到es

EsCarService esCarService = EsCarService.getEsServiceBuidler().setServiceId(map.get("serviceId"))
                                                              .setUserId(map.get("userId"))
                                                              .setPinPublishRole(pinPublishRole)
                                                              .setCurrentAddress(startPointLat+","+startPointLon)
                                                              .setPinLeaveWords(second.getPinLeaveWords())
                                                              .setPinStartPoint(second.getPinStartPoint())
                                                              .setPinEndPoint(second.getPinEndPoint())
                                                              .setPinServiceAccount(second.getPinServiceAccount())
                                                              .setPinTotalAccount(second.getPinTotalAccount())
                                                              .setPinServiceCode(second.getPinServiceCode())
                                                              .setPinTotalDistance(second.getPinTotalDistance())
                                                              .setPasserRequireSeats(second.getPasserRequireSeats())
                                                              .setLeaveOffTime(second.getLeaveOffTime())
                                                              .setPinStartLatitude(second.getPinStartLatitude())
                                                              .setPinStartLongitude(second.getPinStartLongitude())
                                                              .setPinEndLatitude(second.getPinEndLatitude())
                                                              .setPinEndLongitude(second.getPinEndLongitude())
                                                              .build();
//将行程信息存入es
esPinCarServiceRespority.save(esCarService);

乘客搜索附近车主订单

GeoDistanceQueryBuilder builder = QueryBuilders.geoDistanceQuery(EsProperties.ES_SERVICE_GEO_ADDRESS_FILED).point(lat, lon)
        .distance(EsProperties.ES_SERVICE_SEARCH_DISTANCE_RANGE, DistanceUnit.KILOMETERS);

GeoDistanceSortBuilder sortBuilder = SortBuilders.geoDistanceSort(EsProperties.ES_SERVICE_GEO_ADDRESS_FILED)
        .point(lat, lon)
        .unit(DistanceUnit.KILOMETERS)
        .order(SortOrder.ASC)
        .geoDistance(GeoDistance.ARC);
List<Map<String,Object>> ownerServiceList = EsClient.getQueryListByField(EsProperties.ES_SERVICE_INDEX,
        EsProperties.ES_SERVICE_TYPE,
        EsProperties.ES_SERVICE_FILTER_QUERY_NAME,
        EsProperties.ES_SERVICE_SEARCH_CAR_OWNER,
        sortBuilder,
        builder,pageNo,pageSize);

乘客距离车主的距离差是es自动计算出来的

好了,我是张星,欢迎加入博主技术交流群,群号:313145288

猜你喜欢

转载自blog.csdn.net/zhangxing52077/article/details/80631549