diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/GeoCommandStatistics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/GeoCommandStatistics.java new file mode 100644 index 000000000..93cd6db53 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/GeoCommandStatistics.java @@ -0,0 +1,72 @@ +/* + * Copyright 2016 the original author or authors. + * + * 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.springframework.data.mongodb.core; + +import org.springframework.util.Assert; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; + +/** + * Value object to mitigate different representations of geo command execution results in MongoDB. + * + * @author Oliver Gierke + * @soundtrack Fruitcake - Jeff Coffin (The Inside of the Outside) + */ +class GeoCommandStatistics { + + private static final GeoCommandStatistics NONE = new GeoCommandStatistics(new BasicDBObject()); + + private final DBObject source; + + /** + * Creates a new {@link GeoCommandStatistics} instance with the given source document. + * + * @param source must not be {@literal null}. + */ + private GeoCommandStatistics(DBObject source) { + + Assert.notNull(source, "Source document must not be null!"); + this.source = source; + } + + /** + * Creates a new {@link GeoCommandStatistics} from the given command result extracting the statistics. + * + * @param commandResult must not be {@literal null}. + * @return + */ + public static GeoCommandStatistics from(DBObject commandResult) { + + Assert.notNull(commandResult, "Command result must not be null!"); + + Object stats = commandResult.get("stats"); + return stats == null ? NONE : new GeoCommandStatistics((DBObject) stats); + } + + /** + * Returns the average distance reported by the command result. Mitigating a removal of the field in case the command + * didn't return any result introduced in MongoDB 3.2 RC1. + * + * @return + * @see https://jira.mongodb.org/browse/SERVER-21024 + */ + public double getAverageDistance() { + + Object averageDistance = source.get("avgDistance"); + return averageDistance == null ? Double.NaN : (Double) averageDistance; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 387d5c890..f5757d113 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -669,9 +669,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { return new GeoResults(result, near.getMetric()); } - DBObject stats = (DBObject) commandResult.get("stats"); - double averageDistance = stats == null ? 0 : (Double) stats.get("avgDistance"); - return new GeoResults(result, new Distance(averageDistance, near.getMetric())); + GeoCommandStatistics stats = GeoCommandStatistics.from(commandResult); + return new GeoResults(result, new Distance(stats.getAverageDistance(), near.getMetric())); } public T findAndModify(Query query, Update update, Class entityClass) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/GeoCommandStatisticsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/GeoCommandStatisticsUnitTests.java new file mode 100644 index 000000000..7fd4a093e --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/GeoCommandStatisticsUnitTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016 the original author or authors. + * + * 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.springframework.data.mongodb.core; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.mongodb.BasicDBObject; + +/** + * Unit tests for {@link GeoCommandStatistics}. + * + * @author Oliver Gierke + * @soundtrack Fruitcake - Jeff Coffin (The Inside of the Outside) + */ +public class GeoCommandStatisticsUnitTests { + + /** + * @see DATAMONGO-1361 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNullCommandResult() { + GeoCommandStatistics.from(null); + } + + /** + * @see DATAMONGO-1361 + */ + @Test + public void fallsBackToNanIfNoAverageDistanceIsAvailable() { + + GeoCommandStatistics statistics = GeoCommandStatistics.from(new BasicDBObject("stats", null)); + assertThat(statistics.getAverageDistance(), is(Double.NaN)); + + statistics = GeoCommandStatistics.from(new BasicDBObject("stats", new BasicDBObject())); + assertThat(statistics.getAverageDistance(), is(Double.NaN)); + } + + /** + * @see DATAMONGO-1361 + */ + @Test + public void returnsAverageDistanceIfPresent() { + + GeoCommandStatistics statistics = GeoCommandStatistics + .from(new BasicDBObject("stats", new BasicDBObject("avgDistance", 1.5))); + + assertThat(statistics.getAverageDistance(), is(1.5)); + } +}