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