elasticsearch 中的地理信息存储, 有geo_point形式和geo_shape两种形式
此篇只叙述geo_point,
地理位置需要声明为特殊的类型, 不显示在mapping中定义的话, 需要
{ "pin" : { "location" : { "lat" : 40.12, "lon" : -71.34 }, "tag" : ["food", "family"], "text" : "my favorite family restaurant" } }
如果仍然要显示的在mapping中定义, 则需要将其声明为 geo_point格式
{ "pin" : { "properties" : { "location" : { "type" : "geo_point" } } } }
es的类型有: string, long, date, geo_point, 以后知道了在补充, text(for binary),
range(integer_range, float_range, long_range, double_range, date_range)
boolean, geo_point, geo_shape, ip, keyword, nested, token_count.. 可以参见这儿
多说一句: location的数据存放有3种形式:
1), location: lat + "," + lon // 最开始用的这个, 但是做 geoHashCellQuery查询测试时, 报错了 2) location: { "lat": ..., "lon": ... } // 这个是我使用的导入方式 3), location: [lon, lat] // 这个没用, 没测试, 没发言权
1, 导入查询数据, 使用的建立mapping的方式, 因为需要声明ik分词器
package com.iwhere.geosearch; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.yaml.snakeyaml.tokens.StreamStartToken; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; /** * 从json文件中导入数据到es中 * @author 231 * */ public final class ImportData { private TransportClient client; @Test public void test1() throws Exception { createMapping("test", "catchModel"); importData("test", "catchModel"); System.out.println("success"); } /** * 插入https://www.elastic.co/blog/geo-location-and-search的测试数据 * @throws Exception */ @Test public void test2() throws Exception { String index = "geo"; String type = "test"; BulkRequestBuilder prepareBulk = client.prepareBulk(); for (int i = 0; i < 50; i++) { XContentBuilder source = getJson(40 + i, -71.34 + i, "my favorite family restaurant"); prepareBulk.add(client.prepareIndex(index, type).setSource(source)); } BulkResponse response = prepareBulk.execute().actionGet(); } public XContentBuilder getJson(double lat, double lon, String text) throws IOException { return XContentFactory.jsonBuilder() .startObject() .startObject("pin") .startObject("location").field("lat", lat).field("lon", lon).endObject() .field("tag", "food", "family") .field("text", text) .endObject() .endObject(); } /** * 导入数据 * @throws Exception */ public void importData(String index, String type) throws Exception { BufferedReader br = new BufferedReader(new FileReader(new File("D://catchModel.json"))); StringBuilder sb = new StringBuilder(); String line = null; while((line = br.readLine()) != null) { sb.append(line); } BulkRequestBuilder prepareBulk = client.prepareBulk(); JSONArray parseArray = JSON.parseArray(sb.toString()); for (Object object : parseArray) { // 强转为map, 否则报错 the number of object passed must be even Map<String, Object> source = (Map<String, Object>) object; // IndexResponse response = client.prepareIndex(index, type).setSource(source).get(); XContentBuilder xContentBuilder = XContentFactory.jsonBuilder() .startObject() .field("taskName", source.get("taskName")) .field("sessionId", source.get("sessionId")) .field("geoNum", source.get("geoNum")) .field("geoLevel", source.get("geoLevel")) .field("createTime", source.get("createTime")) .field("endTime", source.get("endTime")) .startObject("location").field("lat", source.get("lbLat")) .field("lon", source.get("lbLng")) // .field("location", source.get("lbLat") + "," + source.get("lbLng")) // XContentFactory.jsonBuilder() // .startObject() // .field("lat", source.get("lbLat")) // .field("lon", source.get("lblng")) // .endObject()) .endObject(); prepareBulk.add(client.prepareIndex(index, type).setSource(xContentBuilder)); } BulkResponse response = prepareBulk.get(); System.out.println(response); } /** * 创建mapping, 添加ik分词器等, 相当于创建数据库表 * 索引库名: indices * 类型 : mappingType * field("indexAnalyzer", "ik"): 字段分词ik索引 * field("searchAnalyzer", "ik"): ik分词查询 * @throws Exception */ public void createMapping(String indices, String type) throws Exception { // 创建index Map<String, Object> settings = new HashMap<>(); settings.put("number_of_shards", 4); // 分片数量 settings.put("number_of_replicas", 0); // 复制数量, 导入时最好为0, 之后2-3即可 settings.put("refresh_interval", "10s");// 刷新时间 CreateIndexRequestBuilder prepareCreate = client.admin().indices().prepareCreate(indices); prepareCreate.setSettings(settings); // 创建mapping XContentBuilder mapping = XContentFactory.jsonBuilder() .startObject() .startObject(type) // .startObject("_ttl")//有了这个设置,就等于在这个给索引的记录增加了失效时间, // //ttl的使用地方如在分布式下,web系统用户登录状态的维护. // .field("enabled", true)//默认的false的 // .field("default", "5m")//默认的失效时间,d/h/m/s 即天/小时/分钟/秒 // .field("store", "yes") // .field("index", "not_analyzed") // .endObject() // .startObject("_timestamp")//这个字段为时间戳字段.即你添加一条索引记录后,自动给该记录增加个时间字段(记录的创建时间),搜索中可以直接搜索该字段. // .field("enabled", true) // .field("store", "no") // .field("index", "not_analyzed") // .endObject() .startObject("properties") .startObject("taskName").field("type", "string").field("analyzer", "ik").field("searchAnalyzer", "ik").endObject() .startObject("sessionId").field("type", "string").endObject() .startObject("geoNum").field("type", "string").endObject() .startObject("grandPaGeoNum").field("type", "string").endObject() .startArray("sonGeoNum").endArray() .startObject("geoLevel").field("type", "long").endObject() .startObject("state").field("type", "long").endObject() .startObject("createTime").field("type", "date").endObject() .startObject("endTime").field("type", "date").endObject() .startObject("location") .field("type", "geo_point").field("geohash_prefix", true).field("geohash_precision", "1km").endObject()/*.field("lat_lon", true)*/.endObject() .endObject().endObject(); System.out.println(mapping.string()); // PutMappingResponse actionGet = client.admin().|indices().preparePutMapping(indices).setType(indices).setSource(mapping).execute().actionGet(); prepareCreate.addMapping(type, mapping); CreateIndexResponse response = prepareCreate.execute().actionGet(); System.out.println(response); } @Before public void before() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/applicationContxt-escluster.xml"); client = context.getBean(TransportClient.class); } }
2, 地理位置查询
package com.iwhere.geosearch; import java.net.InetSocketAddress; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.index.query.GeoBoundingBoxQueryBuilder; import org.elasticsearch.index.query.GeoDistanceQueryBuilder; import org.elasticsearch.index.query.GeoDistanceRangeQueryBuilder; import org.elasticsearch.index.query.GeoPolygonQueryBuilder; import org.elasticsearch.index.query.GeoShapeQueryBuilder; import org.elasticsearch.index.query.GeohashCellQuery.Builder; import org.elasticsearch.index.query.QueryBuilders; import org.junit.Before; import org.junit.Test; /** * 使用dsl查询 * @author 231 */ public class JavaESGEO { private TransportClient client; /** * Caused by: [test] QueryParsingException[Field [location] is not a geo_shape]
* 报错, 没运行出来 */ @Test public void testGeoShapeQuery() { GeoShapeQueryBuilder geoShapeQuery = QueryBuilders.geoShapeQuery("location", ShapeBuilder.newMultiPoint() .point(0, 0) .point(0, 10) .point(10, 10) .point(10, 0) .point(0, 0) , ShapeRelation.WITHIN); System.out.println(geoShapeQuery); SearchResponse response = client.prepareSearch("geo") .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(geoShapeQuery).get(); } /** * Caused by: java.lang.IllegalStateException: Shape with name [AVqw3mb-kOe4Yke4p-lh] found but missing shape field */ @Test public void testGeoShapeQuery1() { GeoShapeQueryBuilder queryBuilder = QueryBuilders.geoShapeQuery( "model.location", // field "AVqxrMyikOe4Yke4p_Wx", // id of document "catchModel", ShapeRelation.WITHIN) // type, relation .indexedShapeIndex("test") // name of index .indexedShapePath("location"); // filed specified as path SearchResponse response = client.prepareSearch() .setQuery(queryBuilder).execute().actionGet(); String string = response.getHits().getHits().toString(); System.out.println(string); } /** * 使用 BoundingBoxQuery进行查询 */ @Test public void testGeoBoundingBoxQuery( ){ GeoBoundingBoxQueryBuilder queryBuilder = QueryBuilders.geoBoundingBoxQuery("location") .topRight(40.0, 117) .bottomLeft(39.9, 116); SearchResponse searchResponse = client.prepareSearch("test") .setQuery(queryBuilder).get(); System.out.println(searchResponse); System.err.println(searchResponse.getHits().totalHits()); } /** * distance query 查询 */ @Test public void testDistanceQuery() { GeoDistanceQueryBuilder queryBuilder = QueryBuilders.geoDistanceQuery("location") .point(40, 116.5) .distance(20, DistanceUnit.KILOMETERS) .optimizeBbox("memory") .geoDistance(GeoDistance.ARC); SearchResponse response = client.prepareSearch("geo", "test") .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(queryBuilder).execute().actionGet(); System.out.println(response); System.err.println(response.getHits().totalHits()); } /** * 环形查询 */ @Test public void testDistanceRangeQuery() { GeoDistanceRangeQueryBuilder queryBuilder = QueryBuilders.geoDistanceRangeQuery("location") .point(40, 116.5) // 中心点 .from("20km") // 内环 .to("25km") //外环 .includeLower(true) // 包含上届 .includeUpper(true) // 包含下届 .optimizeBbox("memory") // 边界框 .geoDistance(GeoDistance.SLOPPY_ARC); SearchResponse response = client.prepareSearch("test") .setSearchType(SearchType.DFS_QUERY_AND_FETCH) .setQuery(queryBuilder).execute().actionGet(); System.out.println(response); System.out.println(response.getHits().totalHits()); } /** * 多边形查询 */ @Test public void testPolygonQuery() { GeoPolygonQueryBuilder queryBuilder = QueryBuilders.geoPolygonQuery("location") .addPoint(39, 116) .addPoint(39, 117) .addPoint(40, 117); SearchResponse response = client.prepareSearch("test", "geo") .setQuery(queryBuilder).get(); System.out.println(response); System.err.println(response.getHits().totalHits()); } /** * geoHash查询 * 要使用, 需要先开启 * "location": { "type": "geo_point", "geohash_prefix": true, "geohash_precision": "1km" // 精度, 可在mapping中指定, 也可在代码中指定 */ @Test public void testGeoHashCellQuery() { Builder precision = QueryBuilders.geoHashCellQuery("location", new GeoPoint(39.9, 116)) .neighbors(true) .precision(3); SearchResponse response = client.prepareSearch("test") .setQuery(precision).get(); System.out.println(response); System.err.println(response.getHits().totalHits()); } @Before public void testBefore() { Settings settings = Settings.settingsBuilder().put("cluster.name", "wenbronk_escluster") .put("client.transport.sniff", true).build(); client = TransportClient.builder().settings(settings).build() .addTransportAddress(new InetSocketTransportAddress(new InetSocketAddress("192.168.50.37", 9300))); System.out.println("success to connect escluster"); } }
其他配置信息见: spring整合java一章
基础数据从mongodb中拷贝来的, 在github有一个小量的数据