【问题标题】:How to use geo_point with geohash in Elasticsearch with a Java class?如何在 Elasticsearch 中通过 Java 类使用 geo_point 和 geohash?
【发布时间】:2015-06-14 01:05:22
【问题描述】:

我有一个如下所示的 Java 类(GeoPoint 是 Elasticsearch 类型):

private Long id;
private Integer genre;
private String cityName;
private GeoPoint geoPoint;
private Date lastUpdate;
private Double lat;
private Double lon;

我使用的 Elasticsearch 映射是:

{
    "location": {
        "properties": {
            "id": {"type": "long"},
            "genre": {"type": "integer"},
            "cityName": {"type": "string"},
            "geoPoint": {
                "type": "geo_point",
                "geohash": true,
                "geohash_prefix": true,
                "geohash_precision": 7
            },
            "lastUpdate": {"type": "date", format: "yyyy/MM/dd HH:mm:ss"}
        }
    }
}

当试图索引它时,我得到以下异常:

org.elasticsearch.ElasticsearchParseException: 字段必须是 纬度/经度或地理哈希

line 381 of the GeoUtils class 抛出异常。它发生在检查映射类中的双纬度和经度字段之后,就像 GeoPoint 属性一样。

我不明白为什么它不起作用,因为我按照ElasticSearch documentation 的建议将 geoPoint 的字段类型设置为 geo_point。

更新 1

Java 类。

public class UserLocation implements Serializable {

    private Long id;
    private Integer genre;
    private String cityName;
    private GeoPoint geoPoint;
    private Date lastUpdate;

    public UserLocation () {
    }

    public UserLocation (UserLocationLite userLocationLite) {

        this.id = userLocationLite.getId();
        this.genre = userLocationLite.getGenre();
        this.cityName = userLocationLite.getCity();
        this.geoPoint =
                new GeoPoint(userLocationLite.getLatitude(), userLocationLite.getLongitude());
        this.lastUpdate = userLocationLite.getActivity();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getGenre() {
        return genre;
    }

    public void setGenre(Integer genre) {
        this.genre = genre;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public GeoPoint getGeoPoint() {
        return geoPoint;
    }

    public void setGeoPoint(GeoPoint geoPoint) {
        this.geoPoint = geoPoint;
    }

    public Date getLastUpdate() {
        return lastUpdate;
    }

    public void setLastUpdate(Date lastUpdate) {
        this.lastUpdate = lastUpdate;
    }

}

索引方法。

@Override
public boolean save(String id, R r) {

    try {

        return this.transportClient.prepareIndex(this.index, this.type, id)
                .setSource(this.objectMapper.writeValueAsString(r))
                .execute().actionGet().isCreated();
    } catch (JsonProcessingException e) {

        e.printStackTrace();
    }

    return false;
}

其中 R 是为另一个类实现的一种泛型类型,在本例中为 UserLocation 类,其序列化如下。

{"id":40,"genre":1,"cityName":"Madrid","geoPoint":{"lat":42.626595,"lon":-0.488439,"geohash":"ezrm5c0vx832"},"lastUpdate":1402144560000}

更新 2

现在 Java 类结构工作正常。

public class UserLocationSearch implements Serializable {

    private Long id;
    private Integer genre;
    private String cityName;
    @JsonIgnore
    private GeoPoint geoPoint;
    private Date lastUpdate;

    public UserLocationSearch() {

        this.geoPoint = new GeoPoint();
    }

    public UserLocationSearch(UserLocationLite userLocationLite) {

        this.id = userLocationLite.getId();
        this.genre = userLocationLite.getGenre();
        this.cityName = userLocationLite.getCity();
        this.geoPoint =
                new GeoPoint(userLocationLite.getLatitude(), userLocationLite.getLongitude());
        this.lastUpdate = userLocationLite.getActivity();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getGenre() {
        return genre;
    }

    public void setGenre(Integer genre) {
        this.genre = genre;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public GeoPoint getGeoPoint() {
        return geoPoint;
    }

    public void setGeoPoint(GeoPoint geoPoint) {
        this.geoPoint = geoPoint;
    }

public String getGeohash() {
    return this.geoPoint.getGeohash();
}

public void setGeohash(String geohash) {
    this.geoPoint.resetFromGeoHash(geohash);
}

public Date getLastUpdate() {
    return lastUpdate;
}

public void setLastUpdate(Date lastUpdate) {
    this.lastUpdate = lastUpdate;
}

}

但现在我还有一个问题。

如果得到文件。

GET /user/location/40

{
   "_index": "user",
   "_type": "location",
   "_id": "40",
   "_version": 7,
   "found": true,
   "_source": {
      "id": 40,
      "genre": 1,
      "cityName": "Madrid",
      "lastUpdate": 1402144560000,
      "geohash": "ezrm5c28d9x0"
   }
}

geohash 有 12 个字符,但在映射中 geohash 精度设置为 7...

GET /user/location/_mapping

{
   "user": {
      "mappings": {
         "location": {
            "properties": {
               "cityName": {
                  "type": "string"
               },
               "genre": {
                  "type": "integer"
               },
               "geoPoint": {
                  "type": "geo_point",
                  "geohash": true,
                  "geohash_prefix": true,
                  "geohash_precision": 7
               },
               "geohash": {
                  "type": "string"
               },
               "id": {
                  "type": "long"
               },
               "lastUpdate": {
                  "type": "date",
                  "format": "yyyy/MM/dd HH:mm:ss"
               }
            }
         }
      }
   }
}

这意味着它工作不正常?

更新 3

当前课程。

public class UserLocationSearch implements Serializable {

    private Long id;
    private Integer genre;
    private String cityName;
    private Location location;
    private GeoPoint geoPoint;
    private Date lastUpdate;

    public UserLocationSearch() {

    }

    public UserLocationSearch(UserLocationLite userLocationLite) {

        this.id = userLocationLite.getId();
        this.genre = userLocationLite.getGenre();
        this.cityName = userLocationLite.getCity();
        this.location = new Location(userLocationLite.getLatitude(), userLocationLite.getLongitude());
        this.geoPoint = new GeoPoint(this.location.getGeohash());
        this.lastUpdate = userLocationLite.getActivity();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Integer getGenre() {
        return genre;
    }

    public void setGenre(Integer genre) {
        this.genre = genre;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }

    public GeoPoint getGeoPoint() {
        return geoPoint;
    }

    public void setGeoPoint(GeoPoint geoPoint) {
        this.geoPoint = geoPoint;
    }

    public Date getLastUpdate() {
        return lastUpdate;
    }

    public void setLastUpdate(Date lastUpdate) {
        this.lastUpdate = lastUpdate;
    }

    public static class GeoPoint{

        private String geohash;

        public GeoPoint() {

        }

        public GeoPoint(String geohash) {

            this.geohash = geohash;
        }

        public String getGeohash() {
            return geohash;
        }

        public void setGeohash(String geohash) {
            this.geohash = geohash;
        }
    }

    public static class Location{

        private Double lat;
        private Double lon;

        public Location() {

        }

        public Location(Double lat, Double lon) {

            this.lat = lat;
            this.lon = lon;
        }

        public Double getLat() {
            return lat;
        }

        public void setLat(Double lat) {
            this.lat = lat;
        }

        public Double getLon() {
            return lon;
        }

        public void setLon(Double lon) {
            this.lon = lon;
        }

        @JsonIgnore
        public String getGeohash(){

            return new org.elasticsearch.common.geo.GeoPoint(this.lat, this.lon).getGeohash();
        }

    }

}

它的映射。

{
    "location": {
        "properties": {
            "id": {"type": "long"},
            "genre": {"type": "integer"},
            "cityName": {"type": "string"},
            "location": {
                "type": "geo_point",
                "geohash": false
            },
            "geoPoint": {
                "type": "geo_point",
                "geohash": true,
                "geohash_prefix": true,
                "geohash_precision": 7
            },
            "lastUpdate": {"type": "date", format: "yyyy/MM/dd HH:mm:ss"}
        }
    }
}

搜索。

按距离(工作正常)。

GET /user/location/_search

{
  "query": {
    "match_all": {}
  },
  "filter": {
    "geo_distance": {
      "distance": "100km",
      "location": {
        "lat": 42.5,
        "lon": -0.49
      }
    }
  }
}

通过 geohash(也可以正常工作,超过 7 的精度被忽略,因为它是用“geohash_precision: 7”映射的)。

GET /user/location/_search

{
  "query": {
    "filtered": {
      "filter": {
        "geohash_cell": {
          "geoPoint": {
            "geohash": "ezrm5c0y5rh8"
          },
          "neighbors": true, 
          "precision": 7
        }
      }
    }
  }
}

结论

我不明白为什么 org.elasticsearch.common.geo.GeoHashUtils.GeoPoint 类与实际的 Elastic 版本不兼容。

但是,按照@jkbkot 提供的轨迹,我决定实现自己的 GeoPoint 和 Location 类以获得完全兼容性。

【问题讨论】:

  • 不清楚您要索引什么。你能举一个文档的例子吗?或者您是否有要转换为 JSON 的 JavaBean?
  • 我们需要看到能够为您提供帮助的还有R 的源代码,或者甚至更好的输出,例如:System.out.println(this.objectMapper.writeValueAsString(r))
  • 抱歉 jkbkot,昨天我从下面回答了您的问题,但没有看到此评论。我用 R(UserLocation 实例)序列化更新了主要问题。
  • 太好了,我更新了我的答案,包括 Sense 脚本。我真的建议先使用 Sense 进行尝试。

标签: elasticsearch geohashing


【解决方案1】:

以下Sense script 应该让您知道该怎么做:

DELETE location
PUT location
PUT location/location/_mapping
{
"location": {
    "properties": {
        "id": {"type": "long"},
        "genre": {"type": "integer"},
        "cityName": {"type": "string"},
        "geoPoint": {
            "type": "geo_point",
            "geohash": true,
            "geohash_prefix": true,
            "geohash_precision": 7
        },
        "lastUpdate": {"type": "date", format: "yyyy/MM/dd HH:mm:ss"}
    }
}
}
GET location/location/_mapping

PUT location/location/1
{"id":40,"genre":1,"cityName":"Madrid","geoPoint":{"geohash":"ezrm5c0vx832"},"lastUpdate":1402144560000}

GET /location/location/_search
{
    "query" : {
         "match_all": {}
    },
    "filter" : {
        "geo_distance" : {
            "distance" : "40km",
            "geoPoint" : {
                "lat" : 42.5,
                "lon" : -0.49
            }
        }
    }
}

您必须将保存文档的 Java 类转换为 JSON,其结构如下:

{
    "id": 40,
    "genre": 1,
    "cityName": "Madrid",
    "geoPoint": {
        "geohash": "ezrm5c0vx832"
    },
    "lastUpdate": 1402144560000
}

{
    "id": 40,
    "genre": 1,
    "cityName": "Madrid",
    "geoPoint": {
        "lat": 42.626595,
        "lon": -0.488439
    },
    "lastUpdate": 1402144560000
}

所以要么你必须删除例如private GeoPoint geoPoint; 从您的 Java 类中保留 latlon 那里(或相反),或者您必须更改将类序列化为 JSON 字符串的方式 - 省略 geoPoint 或 lat 和 lon。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    相关资源
    最近更新 更多