@Override
  public Mapper parse(ParseContext context) throws IOException {
    ContentPath.Type origPathType = context.path().pathType();
    context.path().pathType(pathType);
    context.path().add(simpleName());

    GeoPoint sparse = context.parseExternalValue(GeoPoint.class);

    if (sparse != null) {
      parse(context, sparse, null);
    } else {
      sparse = new GeoPoint();
      XContentParser.Token token = context.parser().currentToken();
      if (token == XContentParser.Token.START_ARRAY) {
        token = context.parser().nextToken();
        if (token == XContentParser.Token.START_ARRAY) {
          // its an array of array of lon/lat [ [1.2, 1.3], [1.4, 1.5] ]
          while (token != XContentParser.Token.END_ARRAY) {
            parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
            token = context.parser().nextToken();
          }
        } else {
          // its an array of other possible values
          if (token == XContentParser.Token.VALUE_NUMBER) {
            double lon = context.parser().doubleValue();
            token = context.parser().nextToken();
            double lat = context.parser().doubleValue();
            while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) ;
            parse(context, sparse.reset(lat, lon), null);
          } else {
            while (token != XContentParser.Token.END_ARRAY) {
              if (token == XContentParser.Token.VALUE_STRING) {
                parsePointFromString(context, sparse, context.parser().text());
              } else {
                parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
              }
              token = context.parser().nextToken();
            }
          }
        }
      } else if (token == XContentParser.Token.VALUE_STRING) {
        parsePointFromString(context, sparse, context.parser().text());
      } else if (token != XContentParser.Token.VALUE_NULL) {
        parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
      }
    }

    context.path().remove();
    context.path().pathType(origPathType);
    return null;
  }
 private AbstractDistanceScoreFunction parseGeoVariable(
     XContentParser parser,
     QueryShardContext context,
     BaseGeoPointFieldMapper.GeoPointFieldType fieldType,
     MultiValueMode mode)
     throws IOException {
   XContentParser.Token token;
   String parameterName = null;
   GeoPoint origin = new GeoPoint();
   String scaleString = null;
   String offsetString = "0km";
   double decay = 0.5;
   while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
     if (token == XContentParser.Token.FIELD_NAME) {
       parameterName = parser.currentName();
     } else if (DecayFunctionBuilder.SCALE.equals(parameterName)) {
       scaleString = parser.text();
     } else if (DecayFunctionBuilder.ORIGIN.equals(parameterName)) {
       origin = GeoUtils.parseGeoPoint(parser);
     } else if (DecayFunctionBuilder.DECAY.equals(parameterName)) {
       decay = parser.doubleValue();
     } else if (DecayFunctionBuilder.OFFSET.equals(parameterName)) {
       offsetString = parser.text();
     } else {
       throw new ElasticsearchParseException("parameter [{}] not supported!", parameterName);
     }
   }
   if (origin == null || scaleString == null) {
     throw new ElasticsearchParseException(
         "[{}] and [{}] must be set for geo fields.",
         DecayFunctionBuilder.ORIGIN,
         DecayFunctionBuilder.SCALE);
   }
   double scale = DistanceUnit.DEFAULT.parse(scaleString, DistanceUnit.DEFAULT);
   double offset = DistanceUnit.DEFAULT.parse(offsetString, DistanceUnit.DEFAULT);
   IndexGeoPointFieldData indexFieldData = context.getForField(fieldType);
   return new GeoFieldDataScoreFunction(
       origin, scale, decay, offset, getDecayFunction(), indexFieldData, mode);
 }
    @Override
    public Builder fromXContent(QueryParseContext parseContext) throws IOException {
      XContentParser parser = parseContext.parser();

      String fieldName = null;
      String geohash = null;
      Integer levels = null;
      Boolean neighbors = null;
      String queryName = null;
      Float boost = null;

      XContentParser.Token token;
      if ((token = parser.currentToken()) != Token.START_OBJECT) {
        throw new ElasticsearchParseException(
            "failed to parse [{}] query. expected an object but found [{}] instead", NAME, token);
      }

      while ((token = parser.nextToken()) != Token.END_OBJECT) {
        if (token == Token.FIELD_NAME) {
          String field = parser.currentName();

          if (parseContext.isDeprecatedSetting(field)) {
            // skip
          } else if (parseContext.parseFieldMatcher().match(field, PRECISION_FIELD)) {
            token = parser.nextToken();
            if (token == Token.VALUE_NUMBER) {
              levels = parser.intValue();
            } else if (token == Token.VALUE_STRING) {
              double meters =
                  DistanceUnit.parse(parser.text(), DistanceUnit.DEFAULT, DistanceUnit.METERS);
              levels = GeoUtils.geoHashLevelsForPrecision(meters);
            }
          } else if (parseContext.parseFieldMatcher().match(field, NEIGHBORS_FIELD)) {
            parser.nextToken();
            neighbors = parser.booleanValue();
          } else if (parseContext
              .parseFieldMatcher()
              .match(field, AbstractQueryBuilder.NAME_FIELD)) {
            parser.nextToken();
            queryName = parser.text();
          } else if (parseContext
              .parseFieldMatcher()
              .match(field, AbstractQueryBuilder.BOOST_FIELD)) {
            parser.nextToken();
            boost = parser.floatValue();
          } else {
            if (fieldName == null) {
              fieldName = field;
              token = parser.nextToken();
              if (token == Token.VALUE_STRING) {
                // A string indicates either a geohash or a
                // lat/lon
                // string
                String location = parser.text();
                if (location.indexOf(",") > 0) {
                  geohash = GeoUtils.parseGeoPoint(parser).geohash();
                } else {
                  geohash = location;
                }
              } else {
                geohash = GeoUtils.parseGeoPoint(parser).geohash();
              }
            } else {
              throw new ParsingException(
                  parser.getTokenLocation(),
                  "["
                      + NAME
                      + "] field name already set to ["
                      + fieldName
                      + "] but found ["
                      + field
                      + "]");
            }
          }
        } else {
          throw new ElasticsearchParseException(
              "failed to parse [{}] query. unexpected token [{}]", NAME, token);
        }
      }
      Builder builder = new Builder(fieldName, geohash);
      if (levels != null) {
        builder.precision(levels);
      }
      if (neighbors != null) {
        builder.neighbors(neighbors);
      }
      if (queryName != null) {
        builder.queryName(queryName);
      }
      if (boost != null) {
        builder.boost(boost);
      }
      return builder;
    }