Repository: morphiaorg/morphia
Issue: GeoNearBuilder.setNear() creates wrong near query (mixing up latitude & longutide)

When creating an geo spatial aggregation (http://docs.mongodb.org/manual/reference/operator/aggregation/geoNear/), the near field takes the coordinates as [longitude, latitude].

In morphia the GeoNearBuilder method setNear is defined with:

public GeoNearBuilder setNear(final double latitude, final double longitude) {
            this.near = new double[]{latitude, longitude};
            return this;
 }

A copy of this array of double values is then later put into a DBObject (see AggregationPipeline class line 166). Thus, in the corresponding aggregation query, the query would be:

$geoNear: {
        near: [latutide, longitude],
    ...
}

Morphia version 1.0.1, Mongo Java driver version 3.0.3
JDK version 1.8.0_51
MongoDB version 3.0.5


Changed files:
morphia/src/main/java/org/mongodb/morphia/aggregation/GeoNear.java, morphia/src/test/java/org/mongodb/morphia/geo/PlaceWithLegacyCoords.java, morphia/src/test/java/org/mongodb/morphia/aggregation/AggregationTest.java

diff --git a/morphia/src/main/java/org/mongodb/morphia/aggregation/GeoNear.java b/morphia/src/main/java/org/mongodb/morphia/aggregation/GeoNear.java
index 79d408f6140..ba1c54989ec 100644
--- a/morphia/src/main/java/org/mongodb/morphia/aggregation/GeoNear.java
+++ b/morphia/src/main/java/org/mongodb/morphia/aggregation/GeoNear.java
@@ -268,7 +268,7 @@ public final class GeoNear {
          * @return this
          */
         public GeoNearBuilder setNear(final double latitude, final double longitude) {
-            this.near = new double[]{latitude, longitude};
+            this.near = new double[]{longitude, latitude};
             return this;
         }
 
diff --git a/morphia/src/test/java/org/mongodb/morphia/aggregation/AggregationTest.java b/morphia/src/test/java/org/mongodb/morphia/aggregation/AggregationTest.java
index 973bd0629ed..13b121b3a56 100644
--- a/morphia/src/test/java/org/mongodb/morphia/aggregation/AggregationTest.java
+++ b/morphia/src/test/java/org/mongodb/morphia/aggregation/AggregationTest.java
@@ -24,6 +24,8 @@ import org.junit.Test;
 import org.mongodb.morphia.TestBase;
 import org.mongodb.morphia.annotations.Entity;
 import org.mongodb.morphia.annotations.Id;
+import org.mongodb.morphia.geo.City;
+import org.mongodb.morphia.geo.PlaceWithLegacyCoords;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -37,6 +39,7 @@ import static org.mongodb.morphia.aggregation.Group.push;
 import static org.mongodb.morphia.aggregation.Group.sum;
 import static org.mongodb.morphia.aggregation.Projection.divide;
 import static org.mongodb.morphia.aggregation.Projection.projection;
+import static org.mongodb.morphia.geo.GeoJson.point;
 
 public class AggregationTest extends TestBase {
     @Test
@@ -62,13 +65,64 @@ public class AggregationTest extends TestBase {
     }
 
     @Test
-    public void testGeoNear() {
-        // Given
-
-
-        // When
+    public void testGeoNearWithSphericalGeometry() {
+        // given
+        double latitude = 51.5286416;
+        double longitude = -0.1015987;
+        City london = new City("London", point(latitude, longitude));
+        getDs().save(london);
+        City manchester = new City("Manchester", point(53.4722454, -2.2235922));
+        getDs().save(manchester);
+        City sevilla = new City("Sevilla", point(37.3753708, -5.9550582));
+        getDs().save(sevilla);
+
+        getDs().ensureIndexes();
+
+        // when
+        Iterator<City> citiesOrderedByDistanceFromLondon = getDs().createAggregation(City.class)
+                                                                  .geoNear(GeoNear.builder("distance")
+                                                                                    .setNear(latitude, longitude)
+                                                                                    .setSpherical(true)
+                                                                                    .build())
+                                                                  .aggregate(City.class);
+
+        // then
+        Assert.assertTrue(citiesOrderedByDistanceFromLondon.hasNext());
+        Assert.assertEquals(london, citiesOrderedByDistanceFromLondon.next());
+        Assert.assertEquals(manchester, citiesOrderedByDistanceFromLondon.next());
+        Assert.assertEquals(sevilla, citiesOrderedByDistanceFromLondon.next());
+        Assert.assertFalse(citiesOrderedByDistanceFromLondon.hasNext());
+    }
 
-        // Then
+    @Test
+    public void testGeoNearWithLegacyCoords() {
+        // given
+        double latitude = 51.5286416;
+        double longitude = -0.1015987;
+        PlaceWithLegacyCoords london = new PlaceWithLegacyCoords(new double[]{longitude, latitude}, "London");
+        getDs().save(london);
+        PlaceWithLegacyCoords manchester = new PlaceWithLegacyCoords(new double[]{-2.2235922, 53.4722454}, "Manchester");
+        getDs().save(manchester);
+        PlaceWithLegacyCoords sevilla = new PlaceWithLegacyCoords(new double[]{-5.9550582, 37.3753708}, "Sevilla");
+        getDs().save(sevilla);
+
+        getDs().ensureIndexes();
+
+        // when
+        Iterator<PlaceWithLegacyCoords> citiesOrderedByDistanceFromLondon = getDs()
+                .createAggregation(PlaceWithLegacyCoords.class)
+                .geoNear(GeoNear.builder("distance")
+                                .setNear(latitude, longitude)
+                                .setSpherical(false)
+                                .build())
+                .aggregate(PlaceWithLegacyCoords.class);
+
+        // then
+        Assert.assertTrue(citiesOrderedByDistanceFromLondon.hasNext());
+        Assert.assertEquals(london, citiesOrderedByDistanceFromLondon.next());
+        Assert.assertEquals(manchester, citiesOrderedByDistanceFromLondon.next());
+        Assert.assertEquals(sevilla, citiesOrderedByDistanceFromLondon.next());
+        Assert.assertFalse(citiesOrderedByDistanceFromLondon.hasNext());
     }
 
     @Test
diff --git a/morphia/src/test/java/org/mongodb/morphia/geo/PlaceWithLegacyCoords.java b/morphia/src/test/java/org/mongodb/morphia/geo/PlaceWithLegacyCoords.java
index f31c5cfdb68..b8a61768b01 100644
--- a/morphia/src/test/java/org/mongodb/morphia/geo/PlaceWithLegacyCoords.java
+++ b/morphia/src/test/java/org/mongodb/morphia/geo/PlaceWithLegacyCoords.java
@@ -8,14 +8,14 @@ import org.mongodb.morphia.utils.IndexDirection;
 import java.util.Arrays;
 
 @SuppressWarnings("unused")
-class PlaceWithLegacyCoords {
+public class PlaceWithLegacyCoords {
     @Id
     private ObjectId id;
     @Indexed(IndexDirection.GEO2D)
     private double[] location = new double[2];
     private String name;
 
-    PlaceWithLegacyCoords(final double[] location, final String name) {
+    public PlaceWithLegacyCoords(final double[] location, final String name) {
         this.location = location;
         this.name = name;
     }