Mapper: `geo_point` to support passing array of [lat, lon], closes #361.

This commit is contained in:
kimchy 2010-09-07 17:23:06 +03:00
parent c045b4d0ef
commit ad5945f141
4 changed files with 188 additions and 64 deletions

View File

@ -0,0 +1,29 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.mapper.xcontent;
/**
* A marker interface indicating that this mapper can handle array value, and the array
* itself should be passed to it.
*
* @author kimchy (shay.banon)
*/
public interface XContentArrayValueMapperParser {
}

View File

@ -48,7 +48,7 @@ import static org.elasticsearch.index.mapper.xcontent.XContentTypeParsers.*;
*
* @author kimchy (shay.banon)
*/
public class XContentGeoPointFieldMapper implements XContentMapper {
public class XContentGeoPointFieldMapper implements XContentMapper, XContentArrayValueMapperParser {
public static final String CONTENT_TYPE = "geo_point";
@ -232,67 +232,91 @@ public class XContentGeoPointFieldMapper implements XContentMapper {
context.path().pathType(pathType);
context.path().add(name);
boolean added = false;
XContentParser.Token token = context.parser().currentToken();
if (token == XContentParser.Token.VALUE_STRING) {
String value = context.parser().text();
int comma = value.indexOf(',');
if (comma != -1) {
double lat = Double.parseDouble(value.substring(0, comma).trim());
double lon = Double.parseDouble(value.substring(comma + 1).trim());
added = true;
parseLatLon(context, lat, lon);
if (token == XContentParser.Token.START_ARRAY) {
token = context.parser().nextToken();
if (token == XContentParser.Token.START_ARRAY) {
// its an array of array of lat/lon [ [1.2, 1.3], [1.4, 1.5] ]
while (token != XContentParser.Token.END_ARRAY) {
token = context.parser().nextToken();
Double lat = context.parser().doubleValue();
token = context.parser().nextToken();
Double lon = context.parser().doubleValue();
while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) {
}
parseLatLon(context, lat, lon);
token = context.parser().nextToken();
}
} else {
// geo hash
added = true;
parseGeohash(context, value);
}
} else if (token == XContentParser.Token.START_OBJECT) {
String currentName = context.parser().currentName();
Double lat = null;
Double lon = null;
String geohash = null;
while ((token = context.parser().nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentName = context.parser().currentName();
} else if (token.isValue()) {
if (currentName.equals(Names.LAT)) {
lat = context.parser().doubleValue();
} else if (currentName.equals(Names.LON)) {
lon = context.parser().doubleValue();
} else if (currentName.equals(Names.GEOHASH)) {
geohash = context.parser().text();
// its an array of other possible values
if (token == XContentParser.Token.VALUE_NUMBER) {
Double lat = context.parser().doubleValue();
token = context.parser().nextToken();
Double lon = context.parser().doubleValue();
while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) {
}
parseLatLon(context, lat, lon);
} else {
while (token != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.START_OBJECT) {
parseObjectLatLon(context);
} else if (token == XContentParser.Token.VALUE_STRING) {
parseStringLatLon(context);
}
token = context.parser().nextToken();
}
}
}
if (geohash != null) {
added = true;
parseGeohash(context, geohash);
} else if (lat != null && lon != null) {
added = true;
parseLatLon(context, lat, lon);
}
} else if (token == XContentParser.Token.START_ARRAY) {
token = context.parser().nextToken();
Double lat = context.parser().doubleValue();
token = context.parser().nextToken();
Double lon = context.parser().doubleValue();
token = context.parser().nextToken();
while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) {
}
added = true;
parseLatLon(context, lat, lon);
}
if (!added) {
throw new MapperParsingException("failed to find location values for [" + name + "]");
} else if (token == XContentParser.Token.START_OBJECT) {
parseObjectLatLon(context);
} else if (token == XContentParser.Token.VALUE_STRING) {
parseStringLatLon(context);
}
context.path().remove();
context.path().pathType(origPathType);
}
private void parseStringLatLon(ParseContext context) throws IOException {
String value = context.parser().text();
int comma = value.indexOf(',');
if (comma != -1) {
double lat = Double.parseDouble(value.substring(0, comma).trim());
double lon = Double.parseDouble(value.substring(comma + 1).trim());
parseLatLon(context, lat, lon);
} else { // geo hash
parseGeohash(context, value);
}
}
private void parseObjectLatLon(ParseContext context) throws IOException {
XContentParser.Token token;
String currentName = context.parser().currentName();
Double lat = null;
Double lon = null;
String geohash = null;
while ((token = context.parser().nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentName = context.parser().currentName();
} else if (token.isValue()) {
if (currentName.equals(Names.LAT)) {
lat = context.parser().doubleValue();
} else if (currentName.equals(Names.LON)) {
lon = context.parser().doubleValue();
} else if (currentName.equals(Names.GEOHASH)) {
geohash = context.parser().text();
}
}
}
if (geohash != null) {
parseGeohash(context, geohash);
} else if (lat != null && lon != null) {
parseLatLon(context, lat, lon);
}
}
private void parseLatLon(ParseContext context, Double lat, Double lon) throws IOException {
if (enableLatLon) {
context.externalValue(lat);

View File

@ -386,19 +386,24 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
}
private void serializeArray(ParseContext context, String lastFieldName) throws IOException {
XContentParser parser = context.parser();
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.START_OBJECT) {
serializeObject(context, lastFieldName);
} else if (token == XContentParser.Token.START_ARRAY) {
serializeArray(context, lastFieldName);
} else if (token == XContentParser.Token.FIELD_NAME) {
lastFieldName = parser.currentName();
} else if (token == XContentParser.Token.VALUE_NULL) {
serializeNullValue(context, lastFieldName);
} else {
serializeValue(context, lastFieldName, token);
XContentMapper mapper = mappers.get(lastFieldName);
if (mapper != null && mapper instanceof XContentArrayValueMapperParser) {
mapper.parse(context);
} else {
XContentParser parser = context.parser();
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.START_OBJECT) {
serializeObject(context, lastFieldName);
} else if (token == XContentParser.Token.START_ARRAY) {
serializeArray(context, lastFieldName);
} else if (token == XContentParser.Token.FIELD_NAME) {
lastFieldName = parser.currentName();
} else if (token == XContentParser.Token.VALUE_NULL) {
serializeNullValue(context, lastFieldName);
} else {
serializeValue(context, lastFieldName, token);
}
}
}
}

View File

@ -183,4 +183,70 @@ public class LatLonMappingGeoPointTests {
assertThat(doc.doc().getField("point.lon"), notNullValue());
assertThat(doc.doc().getField("point.geohash"), nullValue());
}
@Test public void testLatLonArray() throws Exception {
String mapping = XContentFactory.contentTextBuilder(XContentType.JSON).startObject().startObject("type")
.startObject("properties").startObject("point").field("type", "geo_point").endObject().endObject()
.endObject().endObject().string();
XContentDocumentMapper defaultMapper = XContentMapperTests.newParser().parse(mapping);
ParsedDocument doc = defaultMapper.parse("type", "1", XContentFactory.jsonBuilder()
.startObject()
.startArray("point").value(1.2).value(1.3).endArray()
.endObject()
.copiedBytes());
assertThat(doc.doc().getField("point.lat"), notNullValue());
assertThat(doc.doc().getField("point.lat").getBinaryValue(), nullValue());
assertThat(doc.doc().getField("point.lon"), notNullValue());
assertThat(doc.doc().getField("point.lon").getBinaryValue(), nullValue());
assertThat(doc.doc().getField("point.geohash"), nullValue());
}
@Test public void testLatLonArrayStored() throws Exception {
String mapping = XContentFactory.contentTextBuilder(XContentType.JSON).startObject().startObject("type")
.startObject("properties").startObject("point").field("type", "geo_point").field("store", "yes").endObject().endObject()
.endObject().endObject().string();
XContentDocumentMapper defaultMapper = XContentMapperTests.newParser().parse(mapping);
ParsedDocument doc = defaultMapper.parse("type", "1", XContentFactory.jsonBuilder()
.startObject()
.startArray("point").value(1.2).value(1.3).endArray()
.endObject()
.copiedBytes());
assertThat(doc.doc().getField("point.lat"), notNullValue());
assertThat(doc.doc().getField("point.lat").getBinaryValue(), equalTo(Numbers.doubleToBytes(1.2)));
assertThat(doc.doc().getField("point.lon"), notNullValue());
assertThat(doc.doc().getField("point.lon").getBinaryValue(), equalTo(Numbers.doubleToBytes(1.3)));
assertThat(doc.doc().getField("point.geohash"), nullValue());
}
@Test public void testLatLonArrayArrayStored() throws Exception {
String mapping = XContentFactory.contentTextBuilder(XContentType.JSON).startObject().startObject("type")
.startObject("properties").startObject("point").field("type", "geo_point").field("store", "yes").endObject().endObject()
.endObject().endObject().string();
XContentDocumentMapper defaultMapper = XContentMapperTests.newParser().parse(mapping);
ParsedDocument doc = defaultMapper.parse("type", "1", XContentFactory.jsonBuilder()
.startObject()
.startArray("point")
.startArray().value(1.2).value(1.3).endArray()
.startArray().value(1.4).value(1.5).endArray()
.endArray()
.endObject()
.copiedBytes());
assertThat(doc.doc().getFields("point.lat").length, equalTo(2));
assertThat(doc.doc().getFields("point.lon").length, equalTo(2));
assertThat(doc.doc().getFields("point.lat")[0].getBinaryValue(), equalTo(Numbers.doubleToBytes(1.2)));
assertThat(doc.doc().getFields("point.lon")[0].getBinaryValue(), equalTo(Numbers.doubleToBytes(1.3)));
assertThat(doc.doc().getFields("point.lat")[1].getBinaryValue(), equalTo(Numbers.doubleToBytes(1.4)));
assertThat(doc.doc().getFields("point.lon")[1].getBinaryValue(), equalTo(Numbers.doubleToBytes(1.5)));
assertThat(doc.doc().getField("point.geohash"), nullValue());
}
}