elasticsearch 5.4.3
最近在研究es 地理信息相关的接口,目前来说es提供俩种geo相关的类型:GeoPoint 和GeoShape两种。这里我只研究一下GeoPoint。该类型在V_5_0_0_alpha1版本进行了重大的改动。首先我们看一下这个类型的整个结构:
父类:BaseGeoPointFieldMapper
子类:
1. GeoPointFieldMapper :适用于2.2.0-4.0.0之间的版本
2. LegacyGeoPointFieldMapper:适用于2.2.0版本
3. LatLonPointFieldMapper:适用于4.0.0之后的版本
具体我们可以看一下BaseGeoPointFieldMapper$TypeParser::parse()函数,在索引文当时会首先判断索引创建的版本,然后在根据版本号决定适使用哪个版本的geo_point.
Version indexVersionCreated = parserContext.indexVersionCreated();
if (indexVersionCreated.before(Version.V_2_2_0)) {
builder = new LegacyGeoPointFieldMapper.Builder(name);
} else if (indexVersionCreated.onOrAfter(LatLonPointFieldMapper.LAT_LON_FIELD_VERSION)) {
builder = new LatLonPointFieldMapper.Builder(name);
} else {
builder = new GeoPointFieldMapper.Builder(name);
}
BaseGeoPointFieldMapper
GeoPointFieldMapper base class to maintain backward compatibility
首先来看对于geopoint的默认配置:
public static class Defaults {
public static final boolean ENABLE_LATLON = false;
public static final boolean ENABLE_GEOHASH = false;
public static final boolean ENABLE_GEOHASH_PREFIX = false;
public static final int GEO_HASH_PRECISION = GeoHashUtils.PRECISION;
public static final Explicit<Boolean> IGNORE_MALFORMED = new Explicit<>(false, false);
}
虽然原始设置是这样的,但是如果你在配置的时候。通过如下方式配置geo_point的属性,是报错的,因为在5.0.0版本后已经屏蔽了这些参数
"location": {
"type": "geo_point",
"geohash_prefix": true,
"geohash_precision": "1km"
},
接下来我们来看一下这三个类型的区别。
LegacyGeoPointFieldMapper
这个类型主要用于2.2.0的版本算是远古时期的了。
MappedFieldType geoHashFieldType;
MappedFieldType latFieldType;
MappedFieldType lonFieldType;
在远古版本的geo_point 在lucene并没有一个属于自己的类型,主要是拆分成了三个field进行索引存储。原始数据值是包含了经纬度的坐标值。为了建立geohash索引还会生成一个额外的field geohash,如
private void addGeoHashField(ParseContext context, String geoHash) throws IOException {
LegacyGeoPointFieldType ft = (LegacyGeoPointFieldType)fieldType;
int len = Math.min(ft.geoHashPrecision(), geoHash.length());
int min = ft.isGeoHashPrefixEnabled() ? 1 : len;
for (int i = len; i >= min; i--) {
// side effect of this call is adding the field
geoHashMapper.parse(context.createExternalValueContext(geoHash.substring(0, i)));
}
}
这个位置怪的很,他把每一位的geohash都保存了一个,为什么部直接用prefix来查呢??
这里的point在lucene里面采用的是binarydocalue的形式,我们可以看一下CustomGeoPointDocValuesField的实现:
@verride
public BytesRef binaryValue() {
final byte[] bytes = new byte[points.size() * 16];
int off = 0;
for (Iterator<ObjectCursor<GeoPoint>> it = points.iterator(); it.hasNext(); ) {
final GeoPoint point = it.next().value;
ByteUtils.writeDoubleLE(point.getLat(), bytes, off);
ByteUtils.writeDoubleLE(point.getLon(), bytes, off + 8);
off += 16;
}
return new BytesRef(bytes);
}
这一点在GeoPointFieldMapper会有较大的变动。
GeoPointFieldMapper
这个类型适用与2.2.0至4.0.0的版本, 到底会被转换成geo_point进行处理。
这个属性在lucene底层会以GeoPointField存储。
@Override
protected void parse(ParseContext context, GeoPoint point, String geoHash) throws IOException {
....
if (fieldType().indexOptions() != IndexOptions.NONE || fieldType().stored()) {
context.doc().add(new GeoPointField(fieldType().name(), point.lat(), point.lon(), fieldType()));
}
super.parse(context, point, geoHash);
}
LatLngPointFieldMapper
这个类型适用4.0.0以上的版本。
我们来看一下Parse函数
if (fieldType().indexOptions() != IndexOptions.NONE) {
context.doc().add(new LatLonPoint(fieldType().name(), point.lat(), point.lon()));
}
if (fieldType().stored()) {
context.doc().add(new StoredField(fieldType().name(), point.toString()));
}
if (fieldType.hasDocValues()) {
context.doc().add(new LatLonDocValuesField(fieldType().name(), point.lat(), point.lon()));
}
// if the mapping contains multifields then use the geohash string
if (multiFields.iterator().hasNext()) {
multiFields.parse(this, context.createExternalValueContext(point.geohash()));
}
首先来说,经纬度的索引形式采用的是LatLonPoint,而若开始docalue时,则采用LatLonDocValuesField,此外他还支持multifield。