目录
ES的字段类型:geo_point,可以实现以一个点为中心的
半径查询(geo_distance query)
ES地理位置查询:
- 半径查询(geo_distance query)
- 查询指定矩形内的数据(geo_bounding_box query)
- 查询指定多边形内的数据(geo_polygon query)
官方文档:Geo queries | Elasticsearch Guide [8.14] | Elastic
本案例实现以某个点的经纬,查询半径查询,并计算出其他点和中心点的距离
一、开发环境
|---------------|--------|
| 工具 | 版本 |
| JDK | 21 |
| SpringBoot | 3.2.4 |
| ElasticSearch | 8.13.2 |
二、pom文件
XML
<!-- Elasticsearch Java API Client -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.13.2</version>
</dependency>
三、ES配置文件
properties:
java
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* ES 配置属性
*
* @author meng
* @date 2024-05-07
*/
@Data
@ConfigurationProperties(prefix = "es")
public class ElasticsearchProperties {
/**
* es 请求方式
*/
private String scheme;
/**
* es 集群host ip 地址
*/
private List<String> hosts;
/**
* es 账号
*/
private String username;
/**
* es 密码
*/
private String password;
/**
* es 连接超时时间
*/
private Integer connectTimeOut;
/**
* es socket 连接超时时间
*/
private Integer socketTimeOut;
/**
* es 请求超时时间
*/
private Integer connectionRequestTimeOut;
/**
* es 最大连接数
*/
private Integer maxConnectNum;
/**
* es 每个路由的最大连接数
*/
private Integer maxConnectNumPerRoute;
}
config:
java
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
/**
* ES 配置类,当 es.enable=true 时自动配置
*
* @author meng
* @date 2024-05-07
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(value = ElasticsearchProperties.class)
@ConditionalOnProperty(prefix = "es", name = "enable")
public class ElasticsearchConfig {
@Bean(name = "ElasticsearchClient")
public ElasticsearchClient initClient(ElasticsearchProperties elasticsearchProperties) {
HttpHost[] esHosts = elasticsearchProperties.getHosts().stream().filter(StringUtils::isNotBlank).map(it -> {
String[] split = it.split(":");
return new HttpHost(split[0], Integer.parseInt(split[1]), elasticsearchProperties.getScheme());
}).toArray(HttpHost[]::new);
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(elasticsearchProperties.getUsername(),
elasticsearchProperties.getPassword()));
RestClientBuilder restClientBuilder = RestClient.builder(esHosts);
// 连接延时配置
restClientBuilder.setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(elasticsearchProperties.getConnectTimeOut());
requestConfigBuilder.setSocketTimeout(elasticsearchProperties.getSocketTimeOut());
requestConfigBuilder.setConnectionRequestTimeout(elasticsearchProperties.getConnectionRequestTimeOut());
return requestConfigBuilder;
});
// HttpClient 连接数配置
restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.setMaxConnTotal(elasticsearchProperties.getMaxConnectNum());
httpClientBuilder.setMaxConnPerRoute(elasticsearchProperties.getMaxConnectNumPerRoute());
return httpClientBuilder;
});
// 设置keepalive时间
restClientBuilder.setHttpClientConfigCallback(requestConfig ->
requestConfig.setKeepAliveStrategy((response, context) -> Duration.ofMinutes(3).toMillis())
);
// 忽略HTTPS方式链接,SSL证书、主机名验证器
restClientBuilder.setHttpClientConfigCallback(httpAsyncClientBuilder -> {
httpAsyncClientBuilder.disableAuthCaching();
httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
try {
// 创建一个信任所有证书的 TrustStrategy 策略
TrustStrategy acceptTrustStrategy = (chain, authType) -> true;
// 使用 SSLContextBuilder 创建 SSLContext
SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(null, acceptTrustStrategy).build();
httpAsyncClientBuilder.setSSLContext(sslContext);
} catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
e.printStackTrace();
}
// 忽略主机名验证
httpAsyncClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
return httpAsyncClientBuilder;
});
RestClient restClient = restClientBuilder.build();
// 使用Jackson映射器创建传输层
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper()
);
ElasticsearchClient client = new ElasticsearchClient(transport);
return client;
}
}
四、ES相关字段
五、ES半径查询
具体查询对象,可自行定义,本方法只提供思路,莫直接粘贴使用
java
@Resource
private ElasticsearchClient esClient;
/**
* 功能描述: 根据经纬度判断十米内是否有poi数据
*
* @Param: [request]
* @Author: meng
* @Date: 2024/6/6 19:57
*/
private PoiDTO getPoiNear(PoiRequest request) {
if (Objects.isNull(request.getLongitude()) && Objects.isNull(request.getLatitude())) {
return null;
}
// 中心点查询
GeoLocation geoLocation = new GeoLocation.Builder().latlon(
l -> l.lon(request.getLongitude().doubleValue()).lat(request.getLatitude().doubleValue())).build();
GeoDistanceQuery geoDistanceQuery = GeoDistanceQuery.of(
geoDis -> geoDis.field(PoiIndexConstant.LOCATION).location(geoLocation)
.distance(PoiIndexConstant.DISTANCE).distanceType(GeoDistanceType.Arc));
// 排序
List<SortOptions> sorts = new ArrayList<>();
// 距离排序 米
GeoDistanceSort geoDistanceSort = GeoDistanceSort.of(
geoDis -> geoDis.field(PoiIndexConstant.LOCATION).location(geoLocation).order(SortOrder.Asc)
.unit(DistanceUnit.Meters));
SortOptions sortOptions = SortOptions.of(sort -> sort.geoDistance(geoDistanceSort));
sorts.add(sortOptions);
SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
searchRequestBuilder.index(PoiIndexConstant.INDEX_READ)
.query(query -> query.bool(builder -> builder.filter(f -> f.geoDistance(geoDistanceQuery)))).sort(sorts)
.size(CommonConstant.DEFAULT_NUM);
// ES查询
SearchRequest searchRequest = searchRequestBuilder.build();
log.info("getPoiNear query:{}", searchRequest.toString());
try {
SearchResponse<PoiIndex> searchResponse = esClient.search(searchRequest, PoiIndex.class);
// 未查到数据
if (Objects.nonNull(searchResponse.hits().total()) && searchResponse.hits().total().value() == 0) {
return null;
}
for (Hit<PoiIndex> hit : searchResponse.hits().hits()) {
if (ObjectUtil.isNotNull(hit.source())) {
PoiIndex poiIndex = BeanUtil.copyProperties(hit.source(), PoiIndex.class);
return BeanUtil.copyProperties(poiIndex, PoiDTO.class);
}
}
} catch (Exception e) {
log.error("es searchRequest Exception:", e);
throw new ServiceException("searchRequest Exception");
}
return null;
}