/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.search.aggregations.bucket.range.geodistance;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilderException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Builder for the {@link GeoDistance} aggregation.
*/
public class GeoDistanceBuilder extends AggregationBuilder<GeoDistanceBuilder> {
/**
* A range of values.
*/
public static class Range implements ToXContent {
private String key;
private Double from;
private Double to;
/**
* Create a new range.
* @param key the identifier of this range
* @param from the lower bound (inclusive)
* @param to the upper bound (exclusive)
*/
public Range(String key, Double from, Double to) {
this.key = key;
this.from = from;
this.to = to;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (from != null) {
builder.field("from", from.doubleValue());
}
if (to != null) {
builder.field("to", to.doubleValue());
}
if (key != null) {
builder.field("key", key);
}
return builder.endObject();
}
}
private String field;
private DistanceUnit unit;
private GeoDistance distanceType;
private GeoPoint point;
private List<Range> ranges = new ArrayList<>();
/**
* Sole constructor.
*/
public GeoDistanceBuilder(String name) {
super(name, InternalGeoDistance.TYPE.name());
}
/**
* Set the field to use to compute distances.
*/
public GeoDistanceBuilder field(String field) {
this.field = field;
return this;
}
/**
* Set the unit to use for distances, default is kilometers.
*/
public GeoDistanceBuilder unit(DistanceUnit unit) {
this.unit = unit;
return this;
}
/**
* Set the {@link GeoDistance distance type} to use, defaults to
* {@link GeoDistance#SLOPPY_ARC}.
*/
public GeoDistanceBuilder distanceType(GeoDistance distanceType) {
this.distanceType = distanceType;
return this;
}
/**
* Set the point to calculate distances from using a
* <code>lat,lon</code> notation or geohash.
*/
public GeoDistanceBuilder point(String latLon) {
return point(GeoPoint.parseFromLatLon(latLon));
}
/**
* Set the point to calculate distances from.
*/
public GeoDistanceBuilder point(GeoPoint point) {
this.point = point;
return this;
}
/**
* Set the point to calculate distances from using its geohash.
*/
public GeoDistanceBuilder geohash(String geohash) {
if (this.point == null) {
this.point = new GeoPoint();
}
this.point.resetFromGeoHash(geohash);
return this;
}
/**
* Set the latitude of the point to calculate distances from.
*/
public GeoDistanceBuilder lat(double lat) {
if (this.point == null) {
point = new GeoPoint();
}
point.resetLat(lat);
return this;
}
/**
* Set the longitude of the point to calculate distances from.
*/
public GeoDistanceBuilder lon(double lon) {
if (this.point == null) {
point = new GeoPoint();
}
point.resetLon(lon);
return this;
}
/**
* Add a new range to this aggregation.
*
* @param key the key to use for this range in the response
* @param from the lower bound on the distances, inclusive
* @param to the upper bound on the distances, exclusive
*/
public GeoDistanceBuilder addRange(String key, double from, double to) {
ranges.add(new Range(key, from, to));
return this;
}
/**
* Same as {@link #addRange(String, double, double)} but the key will be
* automatically generated based on <code>from</code> and <code>to</code>.
*/
public GeoDistanceBuilder addRange(double from, double to) {
return addRange(null, from, to);
}
/**
* Add a new range with no lower bound.
*
* @param key the key to use for this range in the response
* @param to the upper bound on the distances, exclusive
*/
public GeoDistanceBuilder addUnboundedTo(String key, double to) {
ranges.add(new Range(key, null, to));
return this;
}
/**
* Same as {@link #addUnboundedTo(String, double)} but the key will be
* computed automatically.
*/
public GeoDistanceBuilder addUnboundedTo(double to) {
return addUnboundedTo(null, to);
}
/**
* Add a new range with no upper bound.
*
* @param key the key to use for this range in the response
* @param from the lower bound on the distances, inclusive
*/
public GeoDistanceBuilder addUnboundedFrom(String key, double from) {
ranges.add(new Range(key, from, null));
return this;
}
/**
* Same as {@link #addUnboundedFrom(String, double)} but the key will be
* computed automatically.
*/
public GeoDistanceBuilder addUnboundedFrom(double from) {
return addUnboundedFrom(null, from);
}
@Override
protected XContentBuilder internalXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (ranges.isEmpty()) {
throw new SearchSourceBuilderException("at least one range must be defined for geo_distance aggregation [" + getName() + "]");
}
if (point == null) {
throw new SearchSourceBuilderException("center point must be defined for geo_distance aggregation [" + getName() + "]");
}
if (field != null) {
builder.field("field", field);
}
if (unit != null) {
builder.field("unit", unit);
}
if (distanceType != null) {
builder.field("distance_type", distanceType.name().toLowerCase(Locale.ROOT));
}
builder.startObject("center")
.field("lat", point.lat())
.field("lon", point.lon())
.endObject();
builder.startArray("ranges");
for (Range range : ranges) {
range.toXContent(builder, params);
}
builder.endArray();
return builder.endObject();
}
}