@Test
  public void testReusableBuilder() throws IOException {
    ShapeBuilder polygon =
        ShapeBuilder.newPolygon()
            .point(170, -10)
            .point(190, -10)
            .point(190, 10)
            .point(170, 10)
            .hole()
            .point(175, -5)
            .point(185, -5)
            .point(185, 5)
            .point(175, 5)
            .close()
            .close();
    assertUnmodified(polygon);

    ShapeBuilder linestring =
        ShapeBuilder.newLineString().point(170, -10).point(190, -10).point(190, 10).point(170, 10);
    assertUnmodified(linestring);
  }
 @Override
 public Mapper parse(ParseContext context) throws IOException {
   try {
     Shape shape = context.parseExternalValue(Shape.class);
     if (shape == null) {
       ShapeBuilder shapeBuilder = ShapeBuilder.parse(context.parser(), this);
       if (shapeBuilder == null) {
         return null;
       }
       shape = shapeBuilder.build();
     }
     if (fieldType().pointsOnly() && !(shape instanceof Point)) {
       throw new MapperParsingException(
           "[{"
               + fieldType().names().fullName()
               + "}] is configured for points only but a "
               + ((shape instanceof JtsGeometry)
                   ? ((JtsGeometry) shape).getGeom().getGeometryType()
                   : shape.getClass())
               + " was found");
     }
     Field[] fields = fieldType().defaultStrategy().createIndexableFields(shape);
     if (fields == null || fields.length == 0) {
       return null;
     }
     for (Field field : fields) {
       if (!customBoost()) {
         field.setBoost(fieldType().boost());
       }
       context.doc().add(field);
     }
   } catch (Exception e) {
     throw new MapperParsingException(
         "failed to parse [" + fieldType().names().fullName() + "]", e);
   }
   return null;
 }
 @Override
 public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
     throws MapperParsingException {
   Builder builder = geoShapeField(name);
   for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator();
       iterator.hasNext(); ) {
     Map.Entry<String, Object> entry = iterator.next();
     String fieldName = Strings.toUnderscoreCase(entry.getKey());
     Object fieldNode = entry.getValue();
     if (Names.TREE.equals(fieldName)) {
       builder.fieldType().setTree(fieldNode.toString());
       iterator.remove();
     } else if (Names.TREE_LEVELS.equals(fieldName)) {
       builder.fieldType().setTreeLevels(Integer.parseInt(fieldNode.toString()));
       iterator.remove();
     } else if (Names.TREE_PRESISION.equals(fieldName)) {
       builder
           .fieldType()
           .setPrecisionInMeters(
               DistanceUnit.parse(
                   fieldNode.toString(), DistanceUnit.DEFAULT, DistanceUnit.DEFAULT));
       iterator.remove();
     } else if (Names.DISTANCE_ERROR_PCT.equals(fieldName)) {
       builder.fieldType().setDistanceErrorPct(Double.parseDouble(fieldNode.toString()));
       iterator.remove();
     } else if (Names.ORIENTATION.equals(fieldName)) {
       builder
           .fieldType()
           .setOrientation(ShapeBuilder.orientationFromString(fieldNode.toString()));
       iterator.remove();
     } else if (Names.STRATEGY.equals(fieldName)) {
       builder.fieldType().setStrategyName(fieldNode.toString());
       iterator.remove();
     } else if (Names.COERCE.equals(fieldName)) {
       builder.coerce(nodeBooleanValue(fieldNode));
       iterator.remove();
     } else if (Names.STRATEGY_POINTS_ONLY.equals(fieldName)
         && builder.fieldType().strategyName.equals(SpatialStrategy.TERM.getStrategyName())
             == false) {
       builder.fieldType().setPointsOnly(XContentMapValues.nodeBooleanValue(fieldNode));
       iterator.remove();
     }
   }
   return builder;
 }
  @Test
  public void testIndexPointsFilterRectangle() throws Exception {
    String mapping =
        XContentFactory.jsonBuilder()
            .startObject()
            .startObject("type1")
            .startObject("properties")
            .startObject("location")
            .field("type", "geo_shape")
            .field("tree", "quadtree")
            .endObject()
            .endObject()
            .endObject()
            .endObject()
            .string();
    prepareCreate("test").addMapping("type1", mapping).execute().actionGet();
    ensureGreen();

    client()
        .prepareIndex("test", "type1", "1")
        .setSource(
            jsonBuilder()
                .startObject()
                .field("name", "Document 1")
                .startObject("location")
                .field("type", "point")
                .startArray("coordinates")
                .value(-30)
                .value(-30)
                .endArray()
                .endObject()
                .endObject())
        .execute()
        .actionGet();

    client()
        .prepareIndex("test", "type1", "2")
        .setSource(
            jsonBuilder()
                .startObject()
                .field("name", "Document 2")
                .startObject("location")
                .field("type", "point")
                .startArray("coordinates")
                .value(-45)
                .value(-50)
                .endArray()
                .endObject()
                .endObject())
        .execute()
        .actionGet();

    refresh();
    client().admin().indices().prepareRefresh().execute().actionGet();

    ShapeBuilder shape = ShapeBuilder.newEnvelope().topLeft(-45, 45).bottomRight(45, -45);

    SearchResponse searchResponse =
        client()
            .prepareSearch()
            .setQuery(filteredQuery(matchAllQuery(), geoIntersectionFilter("location", shape)))
            .execute()
            .actionGet();

    assertThat(searchResponse.getHits().getTotalHits(), equalTo(1l));
    assertThat(searchResponse.getHits().hits().length, equalTo(1));
    assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));

    searchResponse =
        client().prepareSearch().setQuery(geoShapeQuery("location", shape)).execute().actionGet();

    assertThat(searchResponse.getHits().getTotalHits(), equalTo(1l));
    assertThat(searchResponse.getHits().hits().length, equalTo(1));
    assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
  }
 private void assertUnmodified(ShapeBuilder builder) throws IOException {
   String before = jsonBuilder().startObject().field("area", builder).endObject().string();
   builder.build();
   String after = jsonBuilder().startObject().field("area", builder).endObject().string();
   assertThat(before, equalTo(after));
 }
  // TODO this test causes hangs, blocking on the action get when fetching the shape for some reason
  @Test
  @AwaitsFix(
      bugUrl =
          "this test causes hangs, blocking on the action get when fetching the shape for some reason")
  public void testIndexedShapeReference() throws Exception {
    String mapping =
        XContentFactory.jsonBuilder()
            .startObject()
            .startObject("type1")
            .startObject("properties")
            .startObject("location")
            .field("type", "geo_shape")
            .field("tree", "quadtree")
            .endObject()
            .endObject()
            .endObject()
            .endObject()
            .string();
    prepareCreate("test").addMapping("type1", mapping).execute().actionGet();
    ensureGreen();

    client()
        .prepareIndex("test", "type1", "1")
        .setSource(
            jsonBuilder()
                .startObject()
                .field("name", "Document 1")
                .startObject("location")
                .field("type", "point")
                .startArray("coordinates")
                .value(-30)
                .value(-30)
                .endArray()
                .endObject()
                .endObject())
        .execute()
        .actionGet();

    refresh();

    ShapeBuilder shape = ShapeBuilder.newEnvelope().topLeft(-45, 45).bottomRight(45, -45);
    XContentBuilder shapeContent = jsonBuilder().startObject().field("shape", shape);
    shapeContent.endObject();
    createIndex("shapes");
    ensureGreen();
    client()
        .prepareIndex("shapes", "shape_type", "Big_Rectangle")
        .setSource(shapeContent)
        .execute()
        .actionGet();
    refresh();

    SearchResponse searchResponse =
        client()
            .prepareSearch("test")
            .setQuery(
                filteredQuery(
                    matchAllQuery(),
                    geoIntersectionFilter("location", "Big_Rectangle", "shape_type")))
            .execute()
            .actionGet();

    assertThat(searchResponse.getHits().getTotalHits(), equalTo(1l));
    assertThat(searchResponse.getHits().hits().length, equalTo(1));
    assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));

    searchResponse =
        client()
            .prepareSearch()
            .setQuery(geoShapeQuery("location", "Big_Rectangle", "shape_type"))
            .execute()
            .actionGet();

    assertThat(searchResponse.getHits().getTotalHits(), equalTo(1l));
    assertThat(searchResponse.getHits().hits().length, equalTo(1));
    assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
  }
  @Test
  public void testEdgeCases() throws Exception {

    String mapping =
        XContentFactory.jsonBuilder()
            .startObject()
            .startObject("type1")
            .startObject("properties")
            .startObject("location")
            .field("type", "geo_shape")
            .field("tree", "quadtree")
            .endObject()
            .endObject()
            .endObject()
            .endObject()
            .string();
    prepareCreate("test").addMapping("type1", mapping).execute().actionGet();
    ensureGreen();

    client()
        .prepareIndex("test", "type1", "blakely")
        .setSource(
            jsonBuilder()
                .startObject()
                .field("name", "Blakely Island")
                .startObject("location")
                .field("type", "polygon")
                .startArray("coordinates")
                .startArray()
                .startArray()
                .value(-122.83)
                .value(48.57)
                .endArray()
                .startArray()
                .value(-122.77)
                .value(48.56)
                .endArray()
                .startArray()
                .value(-122.79)
                .value(48.53)
                .endArray()
                .startArray()
                .value(-122.83)
                .value(48.57)
                .endArray() // close the polygon
                .endArray()
                .endArray()
                .endObject()
                .endObject())
        .execute()
        .actionGet();

    client().admin().indices().prepareRefresh().execute().actionGet();

    ShapeBuilder query =
        ShapeBuilder.newEnvelope().topLeft(-122.88, 48.62).bottomRight(-122.82, 48.54);

    // This search would fail if both geoshape indexing and geoshape filtering
    // used the bottom-level optimization in SpatialPrefixTree#recursiveGetNodes.
    SearchResponse searchResponse =
        client()
            .prepareSearch()
            .setQuery(filteredQuery(matchAllQuery(), geoIntersectionFilter("location", query)))
            .execute()
            .actionGet();

    assertThat(searchResponse.getHits().getTotalHits(), equalTo(1l));
    assertThat(searchResponse.getHits().hits().length, equalTo(1));
    assertThat(searchResponse.getHits().getAt(0).id(), equalTo("blakely"));
  }
  @Test
  public void testExternalValues() throws Exception {
    prepareCreate("test-idx")
        .addMapping(
            "type",
            XContentFactory.jsonBuilder()
                .startObject()
                .startObject("type")
                .startObject(ExternalRootMapper.CONTENT_TYPE)
                .endObject()
                .startObject("properties")
                .startObject("field")
                .field("type", RegisterExternalTypes.EXTERNAL)
                .endObject()
                .endObject()
                .endObject()
                .endObject())
        .execute()
        .get();
    ensureYellow("test-idx");

    index(
        "test-idx",
        "type",
        "1",
        XContentFactory.jsonBuilder().startObject().field("field", "1234").endObject());
    refresh();

    SearchResponse response;

    response =
        client()
            .prepareSearch("test-idx")
            .setPostFilter(FilterBuilders.termFilter("field.bool", "T"))
            .execute()
            .actionGet();

    assertThat(response.getHits().totalHits(), equalTo((long) 1));

    response =
        client()
            .prepareSearch("test-idx")
            .setPostFilter(
                FilterBuilders.geoDistanceRangeFilter("field.point").point(42.0, 51.0).to("1km"))
            .execute()
            .actionGet();

    assertThat(response.getHits().totalHits(), equalTo((long) 1));

    response =
        client()
            .prepareSearch("test-idx")
            .setPostFilter(
                FilterBuilders.geoShapeFilter(
                    "field.shape", ShapeBuilder.newPoint(-100, 45), ShapeRelation.WITHIN))
            .execute()
            .actionGet();

    assertThat(response.getHits().totalHits(), equalTo((long) 1));

    response =
        client()
            .prepareSearch("test-idx")
            .setPostFilter(FilterBuilders.termFilter("field.field", "foo"))
            .execute()
            .actionGet();

    assertThat(response.getHits().totalHits(), equalTo((long) 1));
  }
  @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 // see #3878
 public void testThatXContentSerializationInsideOfArrayWorks() throws Exception {
   EnvelopeBuilder envelopeBuilder = ShapeBuilder.newEnvelope().topLeft(0, 0).bottomRight(10, 10);
   GeoShapeQueryBuilder geoQuery = QueryBuilders.geoShapeQuery("searchGeometry", envelopeBuilder);
   JsonXContent.contentBuilder().startArray().value(geoQuery).endArray();
 }
  @Test
  public void testShapeFilterWithDefinedGeoCollection() throws Exception {
    createIndex("shapes");
    assertAcked(prepareCreate("test").addMapping("type", "location", "type=geo_shape"));

    XContentBuilder docSource =
        jsonBuilder()
            .startObject()
            .startObject("location")
            .field("type", "geometrycollection")
            .startArray("geometries")
            .startObject()
            .field("type", "point")
            .startArray("coordinates")
            .value(100.0)
            .value(0.0)
            .endArray()
            .endObject()
            .startObject()
            .field("type", "linestring")
            .startArray("coordinates")
            .startArray()
            .value(101.0)
            .value(0.0)
            .endArray()
            .startArray()
            .value(102.0)
            .value(1.0)
            .endArray()
            .endArray()
            .endObject()
            .endArray()
            .endObject()
            .endObject();
    indexRandom(true, client().prepareIndex("test", "type", "1").setSource(docSource));
    ensureSearchable("test");

    GeoShapeFilterBuilder filter =
        FilterBuilders.geoShapeFilter(
            "location",
            ShapeBuilder.newGeometryCollection()
                .polygon(
                    ShapeBuilder.newPolygon()
                        .point(99.0, -1.0)
                        .point(99.0, 3.0)
                        .point(103.0, 3.0)
                        .point(103.0, -1.0)
                        .point(99.0, -1.0)),
            ShapeRelation.INTERSECTS);
    SearchResponse result =
        client()
            .prepareSearch("test")
            .setQuery(QueryBuilders.matchAllQuery())
            .setPostFilter(filter)
            .get();
    assertSearchResponse(result);
    assertHitCount(result, 1);
    filter =
        FilterBuilders.geoShapeFilter(
            "location",
            ShapeBuilder.newGeometryCollection()
                .polygon(
                    ShapeBuilder.newPolygon()
                        .point(199.0, -11.0)
                        .point(199.0, 13.0)
                        .point(193.0, 13.0)
                        .point(193.0, -11.0)
                        .point(199.0, -11.0)),
            ShapeRelation.INTERSECTS);
    result =
        client()
            .prepareSearch("test")
            .setQuery(QueryBuilders.matchAllQuery())
            .setPostFilter(filter)
            .get();
    assertSearchResponse(result);
    assertHitCount(result, 0);
    filter =
        FilterBuilders.geoShapeFilter(
            "location",
            ShapeBuilder.newGeometryCollection()
                .polygon(
                    ShapeBuilder.newPolygon()
                        .point(99.0, -1.0)
                        .point(99.0, 3.0)
                        .point(103.0, 3.0)
                        .point(103.0, -1.0)
                        .point(99.0, -1.0))
                .polygon(
                    ShapeBuilder.newPolygon()
                        .point(199.0, -11.0)
                        .point(199.0, 13.0)
                        .point(193.0, 13.0)
                        .point(193.0, -11.0)
                        .point(199.0, -11.0)),
            ShapeRelation.INTERSECTS);
    result =
        client()
            .prepareSearch("test")
            .setQuery(QueryBuilders.matchAllQuery())
            .setPostFilter(filter)
            .get();
    assertSearchResponse(result);
    assertHitCount(result, 1);
  }
  @Test
  public void testIndexedShapeReference() throws Exception {
    String mapping =
        XContentFactory.jsonBuilder()
            .startObject()
            .startObject("type1")
            .startObject("properties")
            .startObject("location")
            .field("type", "geo_shape")
            .field("tree", "quadtree")
            .endObject()
            .endObject()
            .endObject()
            .endObject()
            .string();
    assertAcked(prepareCreate("test").addMapping("type1", mapping));
    createIndex("shapes");
    ensureGreen();

    ShapeBuilder shape = ShapeBuilder.newEnvelope().topLeft(-45, 45).bottomRight(45, -45);

    indexRandom(
        true,
        client()
            .prepareIndex("shapes", "shape_type", "Big_Rectangle")
            .setSource(jsonBuilder().startObject().field("shape", shape).endObject()),
        client()
            .prepareIndex("test", "type1", "1")
            .setSource(
                jsonBuilder()
                    .startObject()
                    .field("name", "Document 1")
                    .startObject("location")
                    .field("type", "point")
                    .startArray("coordinates")
                    .value(-30)
                    .value(-30)
                    .endArray()
                    .endObject()
                    .endObject()));

    SearchResponse searchResponse =
        client()
            .prepareSearch("test")
            .setQuery(geoIntersectionQuery("location", "Big_Rectangle", "shape_type"))
            .execute()
            .actionGet();

    assertSearchResponse(searchResponse);
    assertThat(searchResponse.getHits().getTotalHits(), equalTo(1l));
    assertThat(searchResponse.getHits().hits().length, equalTo(1));
    assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));

    searchResponse =
        client()
            .prepareSearch("test")
            .setQuery(geoShapeQuery("location", "Big_Rectangle", "shape_type"))
            .execute()
            .actionGet();

    assertSearchResponse(searchResponse);
    assertThat(searchResponse.getHits().getTotalHits(), equalTo(1l));
    assertThat(searchResponse.getHits().hits().length, equalTo(1));
    assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
  }