@Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    String fieldName = null;
    ShapeRelation shapeRelation = ShapeRelation.INTERSECTS;
    String strategyName = null;
    ShapeBuilder shape = null;
    FilterCachingPolicy cache = parseContext.autoFilterCachePolicy();
    HashedBytesRef cacheKey = null;
    String filterName = null;

    String id = null;
    String type = null;
    String index = DEFAULTS.INDEX_NAME;
    String shapePath = DEFAULTS.SHAPE_FIELD_NAME;

    XContentParser.Token token;
    String currentFieldName = null;

    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        fieldName = currentFieldName;

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

            token = parser.nextToken();
            if ("shape".equals(currentFieldName)) {
              shape = ShapeBuilder.parse(parser);
            } else if ("relation".equals(currentFieldName)) {
              shapeRelation = ShapeRelation.getRelationByName(parser.text());
              if (shapeRelation == null) {
                throw new QueryParsingException(
                    parseContext.index(), "Unknown shape operation [" + parser.text() + "]");
              }
            } else if ("strategy".equals(currentFieldName)) {
              strategyName = parser.text();
            } else if ("indexed_shape".equals(currentFieldName)
                || "indexedShape".equals(currentFieldName)) {
              while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
                if (token == XContentParser.Token.FIELD_NAME) {
                  currentFieldName = parser.currentName();
                } else if (token.isValue()) {
                  if ("id".equals(currentFieldName)) {
                    id = parser.text();
                  } else if ("type".equals(currentFieldName)) {
                    type = parser.text();
                  } else if ("index".equals(currentFieldName)) {
                    index = parser.text();
                  } else if ("path".equals(currentFieldName)) {
                    shapePath = parser.text();
                  }
                }
              }
              if (id == null) {
                throw new QueryParsingException(
                    parseContext.index(), "ID for indexed shape not provided");
              } else if (type == null) {
                throw new QueryParsingException(
                    parseContext.index(), "Type for indexed shape not provided");
              }
              shape = fetchService.fetch(id, type, index, shapePath);
            } else {
              throw new QueryParsingException(
                  parseContext.index(),
                  "[geo_shape] filter does not support [" + currentFieldName + "]");
            }
          }
        }
      } else if (token.isValue()) {
        if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parseContext.parseFilterCachePolicy();
        } else if ("_cache_key".equals(currentFieldName)) {
          cacheKey = new HashedBytesRef(parser.text());
        } else {
          throw new QueryParsingException(
              parseContext.index(),
              "[geo_shape] filter does not support [" + currentFieldName + "]");
        }
      }
    }

    if (shape == null) {
      throw new QueryParsingException(parseContext.index(), "No Shape defined");
    } else if (shapeRelation == null) {
      throw new QueryParsingException(parseContext.index(), "No Shape Relation defined");
    }

    MapperService.SmartNameFieldMappers smartNameFieldMappers =
        parseContext.smartFieldMappers(fieldName);
    if (smartNameFieldMappers == null || !smartNameFieldMappers.hasMapper()) {
      throw new QueryParsingException(
          parseContext.index(), "Failed to find geo_shape field [" + fieldName + "]");
    }

    FieldMapper fieldMapper = smartNameFieldMappers.mapper();
    // TODO: This isn't the nicest way to check this
    if (!(fieldMapper instanceof GeoShapeFieldMapper)) {
      throw new QueryParsingException(
          parseContext.index(), "Field [" + fieldName + "] is not a geo_shape");
    }

    GeoShapeFieldMapper shapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
    PrefixTreeStrategy strategy = shapeFieldMapper.defaultStrategy();
    if (strategyName != null) {
      strategy = shapeFieldMapper.resolveStrategy(strategyName);
    }

    Filter filter;
    if (strategy instanceof RecursivePrefixTreeStrategy
        && shapeRelation == ShapeRelation.DISJOINT) {
      // this strategy doesn't support disjoint anymore: but it did before, including creating
      // lucene fieldcache (!)
      // in this case, execute disjoint as exists && !intersects
      XBooleanFilter bool = new XBooleanFilter();
      Filter exists = ExistsFilterParser.newFilter(parseContext, fieldName, null);
      Filter intersects =
          strategy.makeFilter(GeoShapeQueryParser.getArgs(shape, ShapeRelation.INTERSECTS));
      bool.add(exists, BooleanClause.Occur.MUST);
      bool.add(intersects, BooleanClause.Occur.MUST_NOT);
      filter = bool;
    } else {
      filter = strategy.makeFilter(GeoShapeQueryParser.getArgs(shape, shapeRelation));
    }

    if (cache != null) {
      filter = parseContext.cacheFilter(filter, cacheKey, cache);
    }

    if (filterName != null) {
      parseContext.addNamedFilter(filterName, filter);
    }

    return filter;
  }
  /**
   * Test that orientation parameter correctly persists across cluster restart
   *
   * @throws IOException
   */
  public void testOrientationPersistence() throws Exception {
    String idxName = "orientation";
    String mapping =
        XContentFactory.jsonBuilder()
            .startObject()
            .startObject("shape")
            .startObject("properties")
            .startObject("location")
            .field("type", "geo_shape")
            .field("orientation", "left")
            .endObject()
            .endObject()
            .endObject()
            .endObject()
            .string();

    // create index
    assertAcked(prepareCreate(idxName).addMapping("shape", mapping));

    mapping =
        XContentFactory.jsonBuilder()
            .startObject()
            .startObject("shape")
            .startObject("properties")
            .startObject("location")
            .field("type", "geo_shape")
            .field("orientation", "right")
            .endObject()
            .endObject()
            .endObject()
            .endObject()
            .string();

    assertAcked(prepareCreate(idxName + "2").addMapping("shape", mapping));
    ensureGreen(idxName, idxName + "2");

    internalCluster().fullRestart();
    ensureGreen(idxName, idxName + "2");

    // left orientation test
    IndicesService indicesService =
        internalCluster().getInstance(IndicesService.class, findNodeName(idxName));
    IndexService indexService = indicesService.indexService(idxName);
    FieldMapper fieldMapper = indexService.mapperService().smartNameFieldMapper("location");
    assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));

    GeoShapeFieldMapper gsfm = (GeoShapeFieldMapper) fieldMapper;
    ShapeBuilder.Orientation orientation = gsfm.orientation();
    assertThat(orientation, equalTo(ShapeBuilder.Orientation.CLOCKWISE));
    assertThat(orientation, equalTo(ShapeBuilder.Orientation.LEFT));
    assertThat(orientation, equalTo(ShapeBuilder.Orientation.CW));

    // right orientation test
    indicesService =
        internalCluster().getInstance(IndicesService.class, findNodeName(idxName + "2"));
    indexService = indicesService.indexService(idxName + "2");
    fieldMapper = indexService.mapperService().smartNameFieldMapper("location");
    assertThat(fieldMapper, instanceOf(GeoShapeFieldMapper.class));

    gsfm = (GeoShapeFieldMapper) fieldMapper;
    orientation = gsfm.orientation();
    assertThat(orientation, equalTo(ShapeBuilder.Orientation.COUNTER_CLOCKWISE));
    assertThat(orientation, equalTo(ShapeBuilder.Orientation.RIGHT));
    assertThat(orientation, equalTo(ShapeBuilder.Orientation.CCW));
  }