/*
* Copyright (c) 2008 - 2013 MongoDB, Inc. <http://mongodb.com>
*
* Licensed 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.mongodb.morphia.aggregation;
import org.mongodb.morphia.geo.Geometry;
import org.mongodb.morphia.geo.GeometryShapeConverter;
import org.mongodb.morphia.geo.Point;
import org.mongodb.morphia.query.Query;
/**
* Outputs documents in order of nearest to farthest from a specified point.
*
* @mongodb.driver.manual reference/operator/aggregation/geoNear/ geoNear
*/
public final class GeoNear {
private final double[] nearLegacy;
private final Geometry nearGeoJson;
private final String distanceField;
private final Long limit;
private final Long maxDocuments;
private final Double maxDistance;
private final Query query;
private final Boolean spherical;
private final Double distanceMultiplier;
private final String includeLocations;
private final Boolean uniqueDocuments;
private GeoNear(final GeoNearBuilder builder) {
nearLegacy = builder.nearLegacy;
nearGeoJson = builder.nearGeoJson;
distanceField = builder.distanceField;
limit = builder.limit;
maxDocuments = builder.maxDocuments;
maxDistance = builder.maxDistance;
query = builder.query;
spherical = builder.spherical;
distanceMultiplier = builder.distanceMultiplier;
includeLocations = builder.includeLocations;
uniqueDocuments = builder.uniqueDocuments;
}
/**
* Creates a builder for a GeoNear pipeline stage
*
* @param distanceField the field to process
* @return the GeoNearBuilder
*/
public static GeoNearBuilder builder(final String distanceField) {
return new GeoNearBuilder(distanceField);
}
/**
* @return the distance field used in this stage
*/
public String getDistanceField() {
return distanceField;
}
/**
* @return the distance multiplier used in this stage
*/
public Double getDistanceMultiplier() {
return distanceMultiplier;
}
/**
* This specifies the output field that identifies the location used to calculate the distance. This option is useful when a location
* field contains multiple locations.
*
* @return the field
*/
public String getIncludeLocations() {
return includeLocations;
}
/**
* @return the maximum number of documents to return
*/
public Long getLimit() {
return limit;
}
/**
* The maximum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall within
* the specified distance from the center point.
*
* @return the maximum
*/
public Double getMaxDistance() {
return maxDistance;
}
/**
* The num option provides the same function as the limit option. Both define the maximum number of documents to return. If both
* options
* are included, the num value overrides the limit value.
*
* @return the maximum
*/
public Long getMaxDocuments() {
return maxDocuments;
}
/**
* The point for which to find the closest documents.
* <p/>
* If using a 2dsphere index, you can specify the point as either a GeoJSON point or legacy coordinate pair.
* <p/>
* If using a 2d index, specify the point as a legacy coordinate pair.
*
* @return the point
*/
public double[] getNear() {
double[] copy = new double[0];
if (nearLegacy != null) {
copy = new double[nearLegacy.length];
System.arraycopy(nearLegacy, 0, copy, 0, nearLegacy.length);
}
return copy;
}
Object getNearAsDBObject(final GeometryShapeConverter.PointConverter pointConverter) {
if (nearGeoJson != null) {
return pointConverter.encode(nearGeoJson);
} else {
return getNear();
}
}
/**
* Limits the results to the documents that match the query.
*
* @return the query
*/
public Query getQuery() {
return query;
}
/**
* Required if using a 2dsphere index. Determines how MongoDB calculates the distance. The default value is false.
* <p/>
* If true, then MongoDB uses spherical geometry to calculate distances in meters if the specified (near) point is a GeoJSON point and
* in radians if the specified (near) point is a legacy coordinate pair.
* <p/>
* If false, then MongoDB uses 2d planar geometry to calculate distance between points.
* <p/>
* If using a 2dsphere index, spherical must be true.
*
* @return true if using spherical geometry
*/
public Boolean getSpherical() {
return spherical;
}
/**
* If this value is true, the query returns a matching document once, even if more than one of the document's location fields match the
* query.
*
* @return true if returning only unique documents
* @deprecated since version MongoDB 2.6: Geospatial queries no longer return duplicate results. The $uniqueDocs operator has no impact
* on results.
*/
@Deprecated
public Boolean getUniqueDocuments() {
return uniqueDocuments;
}
/**
* Provides a builder for GeoNear instances.
*/
public static class GeoNearBuilder {
private final String distanceField;
private Long limit;
private Long maxDocuments;
private Double maxDistance;
private Query query;
private Boolean spherical;
private Double distanceMultiplier;
private String includeLocations;
private Boolean uniqueDocuments;
private double[] nearLegacy;
private Geometry nearGeoJson;
/**
* @param distanceField The output field that contains the calculated distance. To specify a field within a subdocument, use dot
* notation.
* @see <a href="http://docs.mongodb.org/master/reference/glossary/#term-dot-notation">dot notation</a>
*/
public GeoNearBuilder(final String distanceField) {
this.distanceField = distanceField;
}
/**
* @return the GeoNear instance as configured by this builder
*/
public GeoNear build() {
return new GeoNear(this);
}
/**
* The factor to multiply all distances returned by the query. For example, use the distanceMultiplier to convert radians, as
* returned by a spherical query, to kilometers by multiplying by the radius of the Earth.
*
* @param distanceMultiplier the distance multiplier used in this stage
* @return this
*/
public GeoNearBuilder setDistanceMultiplier(final Double distanceMultiplier) {
this.distanceMultiplier = distanceMultiplier;
return this;
}
/**
* This specifies the output field that identifies the location used to calculate the distance. This option is useful when a
* location field contains multiple locations. To specify a field within a subdocument, use dot notation.
*
* @param includeLocations the output field that identifies the location used to calculate the distance
* @return this
* @see <a href="http://docs.mongodb.org/master/reference/glossary/#term-dot-notation">dot notation</a>
*/
public GeoNearBuilder setIncludeLocations(final String includeLocations) {
this.includeLocations = includeLocations;
return this;
}
/**
* The maximum number of documents to return. The default value is 100.
*
* @param limit the maximum
* @return this
* @see #setMaxDocuments(Long).
*/
public GeoNearBuilder setLimit(final Long limit) {
this.limit = limit;
return this;
}
/**
* A distance from the center point. Specify the distance in radians. MongoDB limits the results to those documents that fall
* within
* the specified distance from the center point.
*
* @param maxDistance the maximum
* @return this
*/
public GeoNearBuilder setMaxDistance(final Double maxDistance) {
this.maxDistance = maxDistance;
return this;
}
/**
* The maxDocuments option provides the same function as the limit option. Both define the maximum number of documents to return.
* If
* both options are included, this value overrides the limit value.
*
* @param num the maximum
* @return this
*/
public GeoNearBuilder setMaxDocuments(final Long num) {
this.maxDocuments = num;
return this;
}
/**
* Sets the point for which to find the closest documents.
*
* @param latitude the latitude
* @param longitude the longitude
* @return this
*/
public GeoNearBuilder setNear(final double latitude, final double longitude) {
this.nearLegacy = new double[]{longitude, latitude};
return this;
}
/**
* Sets the point for which to find the closest documents.
*
* @param point a GeoJSON single point location.
* @return this
*/
public GeoNearBuilder setNear(final Point point) {
this.nearGeoJson = point;
return this;
}
/**
* Limits the results to the documents that match the query. The query syntax is the usual MongoDB read operation query syntax.
*
* @param query the query used to limit the documents to consider
* @return this
* @mongodb.driver.manual tutorial/query-documents/ read operation query syntax
*/
public GeoNearBuilder setQuery(final Query query) {
this.query = query;
return this;
}
/**
* If true, MongoDB references points using a spherical surface. The default value is false.
*
* @param spherical true if spherical geometry should be used
* @return this
*/
public GeoNearBuilder setSpherical(final Boolean spherical) {
this.spherical = spherical;
return this;
}
/**
* If this value is true, the query returns a matching document once, even if more than one of the document's location fields match
* the query. If this value is false, the query returns a document multiple times if the document has multiple matching location
* fields. See $uniqueDocs for more information.
*
* @param uniqueDocuments true if only unique documents are required in the return value
* @return this builder
* @see <a href="http://docs.mongodb.org/master/reference/operator/query/uniqueDocs/#op._S_uniqueDocs">uniqueDocs</a>
* @deprecated Deprecated since server version 2.6: Geospatial queries no longer return duplicate results. The $uniqueDocs operator
* has no impact on results.
*/
@Deprecated
public GeoNearBuilder setUniqueDocuments(final Boolean uniqueDocuments) {
this.uniqueDocuments = uniqueDocuments;
return this;
}
}
}