diff --git a/pom.xml b/pom.xml
index 15b2d67f47..17b30d0981 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.3.0.BUILD-SNAPSHOT
+ 2.3.0.DATAMONGO-2059-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml
index c4766040c1..9cf397da49 100644
--- a/spring-data-mongodb-benchmarks/pom.xml
+++ b/spring-data-mongodb-benchmarks/pom.xml
@@ -7,7 +7,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.3.0.BUILD-SNAPSHOT
+ 2.3.0.DATAMONGO-2059-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index ed39c63e76..0c2d12f21b 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.3.0.BUILD-SNAPSHOT
+ 2.3.0.DATAMONGO-2059-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 25cf02b5d5..c54007a4bb 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.3.0.BUILD-SNAPSHOT
+ 2.3.0.DATAMONGO-2059-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java
new file mode 100644
index 0000000000..4504597490
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.bson.Document;
+
+import org.springframework.data.geo.Point;
+import org.springframework.lang.Nullable;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Value object representing a count query. Count queries using {@code $near} or {@code $nearSphere} require a rewrite
+ * to {@code $geoWithin}.
+ *
+ * @author Christoph Strobl
+ * @author Mark Paluch
+ * @since 2.3
+ */
+class CountQuery {
+
+ private Document source;
+
+ private CountQuery(Document source) {
+ this.source = source;
+ }
+
+ public static CountQuery of(Document source) {
+ return new CountQuery(source);
+ }
+
+ /**
+ * Returns the query {@link Document} that can be used with {@code countDocuments()}. Potentially rewrites the query
+ * to be usable with {@code countDocuments()}.
+ *
+ * @return the query {@link Document} that can be used with {@code countDocuments()}.
+ */
+ public Document toQueryDocument() {
+
+ if (!requiresRewrite(source)) {
+ return source;
+ }
+
+ Document target = new Document();
+
+ for (Map.Entry entry : source.entrySet()) {
+
+ if (entry.getValue() instanceof Document && requiresRewrite(entry.getValue())) {
+
+ Document theValue = (Document) entry.getValue();
+ target.putAll(createGeoWithin(entry.getKey(), theValue, source.get("$and")));
+ continue;
+ }
+
+ if (entry.getValue() instanceof Collection && requiresRewrite(entry.getValue())) {
+
+ Collection> source = (Collection>) entry.getValue();
+
+ target.put(entry.getKey(), rewriteCollection(source));
+ continue;
+ }
+
+ if ("$and".equals(entry.getKey()) && target.containsKey("$and")) {
+ // Expect $and to be processed with Document and createGeoWithin.
+ continue;
+ }
+
+ target.put(entry.getKey(), entry.getValue());
+ }
+
+ return target;
+ }
+
+ /**
+ * @param valueToInspect
+ * @return {@code true} if the enclosing element needs to be rewritten.
+ */
+ private boolean requiresRewrite(Object valueToInspect) {
+
+ if (valueToInspect instanceof Document) {
+ return requiresRewrite((Document) valueToInspect);
+ }
+
+ if (valueToInspect instanceof Collection) {
+ return requiresRewrite((Collection) valueToInspect);
+ }
+
+ return false;
+ }
+
+ private boolean requiresRewrite(Collection> collection) {
+
+ for (Object o : collection) {
+ if (o instanceof Document && requiresRewrite((Document) o)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean requiresRewrite(Document document) {
+
+ if (containsNear(document)) {
+ return true;
+ }
+
+ for (Object entry : document.values()) {
+
+ if (requiresRewrite(entry)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private Collection